I'm trying to create a convolution function but I'm having trouble during the access to the kernel data (cv::Mat).
I create the 3x3 kernel:
cv::Mat krn(3, 3, CV_32FC1);
krn.setTo(1);
krn = krn/9;
And I try to loop over it. Next the image
Mat will be the image to which I want to apply the convolution operator and output will be the result of convolution:
for (int r = 0; r < image.rows - krn.rows; r) {
for (int c = 0; c < image.cols - krn.cols; c) {
int sum = 0;
for (int rs = 0; rs < krn.rows; rs) {
for (int cs = 0; cs < krn.cols; cs) {
sum = krn.data[rs * krn.cols cs] * image.data[(r rs) * image.cols c cs];
}
}
output.data[(r 1)*src.cols c 1]=sum; // assuming 3x3 kernel
}
}
However the output is not as desired (only randomic black and white pixel). However, if I change my code this way:
for (int r = 0; r < image.rows - krn.rows; r) {
for (int c = 0; c < image.cols - krn.cols; c) {
int sum = 0;
for (int rs = 0; rs < krn.rows; rs) {
for (int cs = 0; cs < krn.cols; cs) {
sum = 0.11 * image.data[(r rs) * image.cols c cs]; // CHANGE HERE
}
}
output.data[(r 1)*src.cols c 1]=sum; // assuming 3x3 kernel
}
}
Using 0.11
instead of the kernel values seems to give the correct output.
For this reason I think I'm doing something wrong accessing the kernel's data.
P.S: I cannot use krn.at<float>(rs,cs)
.
Thanks!
CodePudding user response:
cv::Mat::data
is pointer of type uchar
.
By data[y * cols x]
you access some byte of stored float
values in krn
. To get full float values use at
method template:
krn.at<float>(rs,cs)
Consider changing type of sum
variable to be real. Without this, you may lose partial results when calculating convolution .
So, if you cannot use at
, just read 4 bytes from data pointer:
float v = 0.0;
memcpy(&v, krn.data (rs * krn.step cs * sizeof(float)), 4);
step
- means total bytes occupied by one line in mat.
CodePudding user response:
Instead of needlessly using memcpy
, you can just cast the pointer. I'll use a C-style cast because why not.
cv::Mat krn = 1 / (cv::Mat_<float>(3,3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9);
for (int i = 0; i < krn.rows; i = 1)
{
for (int j = 0; j < krn.cols; j = 1)
{
// to see clearly what's happening
uint8_t *byteptr = krn.data krn.step[0] * i krn.step[1] * j;
float *floatptr = (float*) byteptr;
// or in one step:
float *floatptr = (float*) (krn.data krn.step[0] * i krn.step[1] * j);
cout << "krn(" << i << "," << j << ") = " << (*floatptr) << endl;
endl;
}
}
krn(0,0) = 1
krn(0,1) = 0.5
krn(0,2) = 0.333333
krn(1,0) = 0.25
krn(1,1) = 0.2
krn(1,2) = 0.166667
krn(2,0) = 0.142857
krn(2,1) = 0.125
krn(2,2) = 0.111111
Note that pointer arithmetic may not be obvious. if you have a uint8_t*
, adding 1
moves it by one uint8_t, and if you have a float*
, adding 1
moves it by one float which is four bytes. The step[]
contains offsets expressed in bytes.
Consult the documentation for details, which include information on the step[]
array that contains the strides/steps to calculate the offset given a tuple of indices into the matrix.