I am using gaussianBlur to get the high pass of the image but it seems like cv.subtract() function does not subtract two images pixel by pixel. I confirmed this by first subtracting the blurred image from input image and then adding the blurred image again to the result. On doing so, the output should be the same as the input image but it is different.
img=src
cv.GaussianBlur(img, blur, new cv.Size(21, 21), 0,0,cv.BORDER_DEFAULT)
let x = new cv.Mat()
let y = new cv.Mat()
cv.subtract(img,blur,x)
cv.add(x,blur,y)
Here are the images for reference input image, blurred image, output image
CodePudding user response:
As commented, cv.add
and cv.subtract
functions clip the uint8
summed/subtracted elements to range [0, 255].
The solution is converting the mat
data type from uint8
to larger data type as int16
.
Example for clipping (pseudocode):
cv.add(uint8(200), uint8(100))
equals uint8(255)
(instead of 300).
cv.subtract(uint8(10), uint8(20))
equals uint8(0)
(instead of -10).
That means that computing: uint8(200)
uint8(100)
- uint8(100)
, results 155
(instead of getting back to the original value 200).
The solution is converting mat
data type from uint8
to larger data type as int16
.
int16
elements are clipped to range [-32768, 32767], and we are not getting close to these range limits.
Computing: int16(200)
int16(100)
- int16(100)
, results 200
(because int16(200) int16(100) = int16(300). No clipping occurs...
Converting from uint8
to int16
is performed using convertTo member function:
img.convertTo(img2, cv.CV_16S);
blur.convertTo(blur2, cv.CV_16S);
Then apply cv.subtract
and cv.add
to the int16
elements:
cv.subtract(img2, blur2, x)
cv.add(x, blur2, y)
At the end, we may convert the result back to uint8
type:
y.convertTo(out, cv.CV_8U);
Here is a sample that applies the above procedure, and verifies that out
= img
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello OpenCV.js</title>
</head>
<body>
<p id="status">OpenCV.js is loading...</p>
<div>
<div >
<img id="imageSrc" alt="No Image" />
<div >imageSrc <input type="file" id="fileInput" name="file" /></div>
</div>
<div >
<canvas id="canvasOutput" ></canvas>
<div >canvasOutput</div>
</div>
</div>
<script async src="opencv.js" type="text/javascript"></script>
<script type="text/javascript">
let imgElement = document.getElementById('imageSrc');
let inputElement = document.getElementById('fileInput');
inputElement.addEventListener('change', (e) => {
imgElement.src = URL.createObjectURL(e.target.files[0]);
}, false);
//read image
imgElement.onload = function () {
let img = cv.imread(imgElement);
let blur = new cv.Mat();
let x = new cv.Mat();
let y = new cv.Mat();
let img2 = new cv.Mat();
let blur2 = new cv.Mat();
let out = new cv.Mat();
cv.GaussianBlur(img, blur, new cv.Size(21, 21), 0, 0, cv.BORDER_DEFAULT);
img.convertTo(img2, cv.CV_16S); //Convert img from type Uint8Array to type Int16Array
blur.convertTo(blur2, cv.CV_16S); //Convert blur from type Uint8Array to type Int16Array
cv.subtract(img2, blur2, x) //Subtract applies Int16 elements (instead of Uint8 elements).
cv.add(x, blur2, y) //Add applies Int16 elements (instead of Uint8 elements).
y.convertTo(out, cv.CV_8U); //Convert y from type Int16Array to type Uint8Array (out is used as input for imshow).
cv.imshow('canvasOutput', out);
let d = new cv.Mat();
cv.absdiff(out, img, d);
//Compute the sum of absolute difference (for testing)
let sum_abs_dif = 0
for (row = 0; row < d.rows; row ) {
for (col = 0; col < d.cols; col ) {
sum_abs_dif = d.ucharPtr(row, col)[0] d.ucharPtr(row, col)[1] d.ucharPtr(row, col)[2];
//d.ucharPtr(row, col)[3] = 255; //Set the alpha (transparency) channel to 255 for testing.
}
}
console.log(sum_abs_dif) //Prints 0
img.delete();
blur.delete();
x.delete();
y.delete();
img2.delete();
blur2.delete();
out.delete();
d.delete();
};
//check openCV
var Module = {
// https://emscripten.org/docs/api_reference/module.html#Module.onRuntimeInitialized
onRuntimeInitialized() {
document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}
};
</script>
</body>
</html>