为何我的Sobel滤波器代码仅返回图像的一半?
问题分析与修复方案
核心问题:图像类型不匹配导致显示异常
你创建filteredImg时指定了CV_16UC1(16位无符号单通道图像),但赋值时却用filteredImg.at<uchar>访问,这会引发内存访问错位。16位图像每个像素占2字节,用8位的uchar访问时,会把每个16位像素拆成两个8位数据,最终显示的图像宽度会变为原来的一半,这就是你看到“仅处理了图像一半”的直接原因。
其他代码问题
- angleMap尺寸错误:初始化时错误使用
(_image.rows - 2) * size和(_image.cols - 2) * size,正确尺寸应与filteredImg一致,即_image.rows - 2*size和_image.cols -2*size。 - 角度计算问题:
atan(sumy/sumx)返回弧度值,需转换为角度(乘以180/M_PI);当sumx=0时,应根据sumy正负判断是90°还是-90°,而非直接设为90°。 - 存储类型冗余:最终结果限制在0-255范围,直接创建
CV_8UC1类型的filteredImg更合理,避免类型转换混乱。
修正后的代码
Mat Picture::sobelFilter() { // Sobel X 滤波核 double x1[] = { -1.0, 0, 1.0 }; double x2[] = { -2.0, 0, 2.0 }; double x3[] = { -1.0, 0, 1.0 }; vector<vector<double>> xFilter(3); xFilter[0].assign(x1, x1 + 3); xFilter[1].assign(x2, x2 + 3); xFilter[2].assign(x3, x3 + 3); // Sobel Y 滤波核 double y1[] = { 1.0, 2.0, 1.0 }; double y2[] = { 0, 0, 0 }; double y3[] = { -1.0, -2.0, -1.0 }; vector<vector<double>> yFilter(3); yFilter[0].assign(y1, y1 + 3); yFilter[1].assign(y2, y2 + 3); yFilter[2].assign(y3, y3 + 3); // 滤波核半尺寸(3x3核的话size=1) int size = (int)xFilter.size() / 2; // 创建8位单通道输出图像,尺寸与滤波后匹配 Mat filteredImg = Mat(_image.rows - 2 * size, _image.cols - 2 * size, CV_8UC1); // 修正angleMap的尺寸,与filteredImg一致 angleMap = Mat(_image.rows - 2 * size, _image.cols - 2 * size, CV_32FC1); for (int i = size; i < _image.rows - size; i++) { for (int j = size; j < _image.cols - size; j++) { double sumx = 0; double sumy = 0; // 应用滤波核计算梯度 for (int x = 0; x < xFilter.size(); x++) for (int y = 0; y < yFilter.size(); y++) { uchar pixel = _image.at<uchar>(i + x - size, j + y - size); sumx += xFilter[x][y] * (double)pixel; sumy += yFilter[x][y] * (double)pixel; } // 计算梯度幅值 double mag = sqrt(sumx*sumx + sumy*sumy); // 幅值截断到0-255范围 mag = (mag > 255) ? 255 : mag; filteredImg.at<uchar>(i - size, j - size) = static_cast<uchar>(mag); // 计算梯度角度(转换为角度值) float angle; if (sumx == 0) { angle = (sumy > 0) ? 90.0f : -90.0f; } else { angle = static_cast<float>(atan2(sumy, sumx) * 180.0 / M_PI); } angleMap.at<float>(i - size, j - size) = angle; } } return filteredImg; }
额外说明
- 确保输入图像
_image是单通道灰度图(CV_8UC1),如果是彩色图需先转换为灰度图再处理,否则_image.at<uchar>访问会出错。 - 改用
atan2(sumy, sumx)替代atan(sumy/sumx),可避免除零问题,同时正确计算四个象限的角度。
内容的提问来源于stack exchange,提问作者Dragon Gali
相关产品推荐
相关产品推荐

