You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何我的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 21:01:21