小记录:关于opencv3.X中的gauss滤波和图像类型转换

本文探讨了在Windows环境下使用OpenCV 3.2和QT 4.8.6时遇到的cvSmooth函数错误,并提供了可能的解决方案及替代方法。同时记录了QImage、Mat和IplImage之间的转换技巧。

**我的开发环境:**windows下opencv3.2,QT4.8.6。
遇到的问题是:smooth = cvCreateImage(cvGetSize(iplImg),IPL_DEPTH_8U,1);
cvSmooth(iplImg,smooth,CV_GAUSSIAN,3,0,0);

**所报错误为:**OpenCV Error: Assertion failed (dst.size() == src.size() && (smooth_type == CV_BLUR_NO_SCALE || dst.type() == src.type()))。程序执行后卡死退出,但是同样的代码在Linux中却可以执行。
应该不是申请内存那块。同样的方式做“灰度化”就没有问题。初步怀疑是编译器问题。同时也请看到这篇文章的大牛们回复具体讨论。
为了达到Gauss滤波的目的先用GaussianBlur函数代替cvSmooth平滑滤波,没有问题。
———-
opencv中的转换挺重要,2.X和3.X有的会有些差别,而且方法不局限一种。因为我的环境是QT,在这里对QImage Mat IplImage转换记录下:
QImage—>IplImage:

QImage* img;
IplImage *iplImg;
int nChannel = 0;
    nChannel = img->format();
    qDebug()<<"img format is:"<<img->format();
    if( nChannel==0 )
    {
        return;
    }
    else
    {
        iplImg=cvCreateImageHeader(cvSize(img->width(),img->height()),8,nChannel);
        iplImg->imageData=(char*)img->bits();
        if(nChannel==3)
        {
            cvConvertImage(iplImg,iplImg,CV_CVTIMG_SWAP_RB);
        }

IplImage—>QImage: 摘自

QImage* CjwIplToQImg(const IplImage *cvImg)
{
if(!cvImg)
return NULL;
IplImage* temp=NULL;
temp=cvCloneImage(cvImg);
cvCvtColor(cvImg,temp,CV_BGR2RGB);
uchar* imgData = (uchar*)(temp->imageData);
QImage *qImg=new QImage(imgData,temp->width,temp->height,QImage::Format_RGB888);
return qImg;
cvReleaseImage(&temp);
delete imgData;
}

IplImage—>Mat:

  IplImage iplImg;
  Mat matimg;
    matimg = cvarrToMat(iplImg);

Mat—>IplImage:

iplImg= IplImage(matimg);

Mat—>QImage:摘自

QImage cvMat2QImage(const cv::Mat& mat)
{
    // 8-bits unsigned, NO. OF CHANNELS = 1
    if(mat.type() == CV_8UC1)
    {
        QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
        // Set the color table (used to translate colour indexes to qRgb values)
        image.setNumColors(256);
        for(int i = 0; i < 256; i++)
        {
            image.setColor(i, qRgb(i, i, i));
        }
        // Copy input Mat
        uchar *pSrc = mat.data;
        for(int row = 0; row < mat.rows; row ++)
        {
            uchar *pDest = image.scanLine(row);
            memcpy(pDest, pSrc, mat.cols);
            pSrc += mat.step;
        }
        return image;
    }
    // 8-bits unsigned, NO. OF CHANNELS = 3
    else if(mat.type() == CV_8UC3)
    {
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
        return image.rgbSwapped();
    }
    else if(mat.type() == CV_8UC4)
    {
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
        return image.copy();
    }
    else
    {
        return QImage();
    }
}

QImage—>Mat:摘自

Mat QImage2cvMat(QImage image)
{
    cv::Mat mat;
    switch(image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32_Premultiplied:
        mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.bits(), image.bytesPerLine());
        break;
    case QImage::Format_RGB888:
        mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.bits(), image.bytesPerLine());
        cv::cvtColor(mat, mat, CV_BGR2RGB);
        break;
    case QImage::Format_Indexed8:
        mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.bits(), image.bytesPerLine());
        break;
    }
    return mat;
}
<think>好的,我现在需要帮助用户解决关于使用OpenCV C++创建滑动条控制方框滤波、均值滤波高斯滤波的代码问题。首先,我得理清用户的需求。用户希望有一个示例代码,其中包含滑动条,可以动态调整这三种滤波的参数,实时查看效果。 首先,我应该回忆一下OpenCV中创建滑动条的函数。记得是createTrackbar,它需要放在一个窗口上,并且需要一个回调函数来处理滑动条的变化。但是,在C++中,回调函数必须是静态函数,这可能会限制对类成员变量的访问,所以可能需要使用全局变量或者单例模式。过用户可能希望一个简单的示例,所以可能先用全局变量来简化代码。 接下来,用户需要控制三种滤波,所以滑动条可能需要选择滤波类型,以及调整参数,比如核大小或者高斯滤波的标准差。但核大小通常是奇数,所以可能需要设置滑动条的范围,例如从1到21,然后取奇数。或者,用户可能希望用滑动条调整模糊程度,比如核的大小,然后根据同的滤波类型应用同的处理。 然后,我需要参考用户提供的引用内容。引用1、2、3都提到了方框滤波、均值滤波高斯滤波的函数使用。例如,方框滤波使用boxFilter,均值滤波是blur,高斯滤波GaussianBlur。同时,方框滤波的normalize参数为true时等同于均值滤波,但用户可能需要分开控制这三种滤波。 用户的问题中希望滑动条控制滤波类型,可能是一个滑动条选择滤波类型,另一个滑动条调整核的大小。或者,可能需要三个滑动条分别控制,但这样界面可能复杂。可能更合理的是用一个滑动条选择滤波类型,另一个调整核大小,或者高斯滤波的标准差。 根据引用3中的代码示例,方框滤波的核大小通过Size(k_size, k_size)设置,而高斯滤波需要指定核大小标准差。均值滤波的核大小也是类似。所以,可能需要滑动条调整核的大小,并且对于高斯滤波,可能固定标准差,或者用另一个滑动条调整。但用户可能希望简化,所以暂时用核大小作为主要参数,对于高斯滤波,可以自动计算标准差,例如根据核大小设置sigma为0,让OpenCV自动计算。 然后,关于回调函数,每次滑动条变化时,需要重新应用选定的滤波并更新图像显示。所以,全局变量可能包括原始图像、当前滤波类型、核大小等。 现在,开始组织代码结构。首先,读取图像,创建窗口,添加滑动条。滤波类型可以用0、1、2分别代表方框滤波、均值滤波高斯滤波。核大小滑动条的值可以映射为奇数,比如2n+1,n从0到某个最大值,比如15,得到1到31的奇数。 在回调函数中,根据当前选择的滤波类型核大小,调用对应的滤波函数。需要注意的是,核大小必须为正奇数,所以当滑动条的值变化时,需要转换为奇数。例如,滑动条的值是0到10,对应的核大小为1,3,5,...,21。 此外,应用滤波时需要处理边缘情况,比如核大小为0或负数,但通过滑动条设置的最小值为1,所以可以避免。但需要确保核大小至少为1,并且在转换为奇数时正确。 另外,方框滤波的normalize参数可能需要考虑。当用户选择方框滤波时,是否需要normalize为true或false?根据引用3,当normalize为true时,方框滤波等同于均值滤波。但用户可能需要区分方框滤波均值滤波,所以可能需要分开处理。但根据用户的问题,可能需要三个独立的滤波选项,所以方框滤波可能使用normalize为false的情况?或者用户希望方框滤波均值滤波分开,但根据引用中的说明,均值滤波是方框滤波的特例。但用户的需求是三个独立的滤波,所以应该分别调用对应的函数:boxFilter(可能normalize为true或false)、blur、GaussianBlur。 可能用户希望的是三个滤波类型,所以滑动条选择类型0、1、2,分别对应方框滤波(normalize=true)、均值滤波高斯滤波。或者方框滤波的normalize参数由另一个滑动条控制?但这样会增加复杂度。可能用户希望的是三个滤波类型,所以代码中固定方框滤波的normalize为true,这样均值滤波分开可能有问题。或者,可能用户希望方框滤波可以选择是否归一化,但根据问题描述,用户可能只需要三个独立的滤波类型,所以可能需要分别处理。 根据引用3中的代码,方框滤波调用boxFilter,而均值滤波调用blur。所以,在代码中,当选择方框滤波时,调用boxFilter,并设置normalize为true;而均值滤波调用blur函数,这其实内部可能也是调用了boxFilter但normalize为true。但为了明确区分,可能用户希望三个同的滤波方式,所以按照函数调用来处理。 现在,编写代码的大致步骤: 1. 读取图像并检查是否成功。 2. 创建显示窗口。 3. 创建滑动条:一个选择滤波类型,另一个调整核大小。 4. 定义回调函数,根据当前滑动条的值应用滤波并显示。 5. 初始化显示,进入主循环等待用户输入。 在代码中,需要注意核大小的处理,必须为正奇数。例如,滑动条的值为k_size,实际核大小为2*k_size +1,或者类似的方式。或者滑动条的范围设置为1到最大,然后强制转换为奇数,例如当用户滑动到偶数时,自动调整为最近的奇数。但这样可能复杂,所以更简单的是滑动条的值对应奇数的大小,比如1,3,5,...,这样滑动条的每个步长对应增加2。但滑动条的步长只能是整数,所以可能需要让滑动条的值对应n,然后核大小为2n+1。例如,n的范围是0到10,对应1到21的奇数。 此外,高斯滤波的核大小必须是正奇数,而标准差sigma可以由核大小自动计算,比如sigma = 0,这样OpenCV会根据核大小自动计算合适的sigma值。 现在,编写示例代码的大纲: 全局变量: Mat src, dst; int filter_type = 0; // 0: box, 1: mean, 2: gaussian int ksize = 1; // 对应奇数的大小,比如实际核大小为2*ksize +1? 回调函数: 当滑动条变化时,获取filter_typeksize的值,计算实际的核大小,然后应用对应的滤波。 例如: void onTrackbar(int, void*) { int current_ksize = 2*ksize +1; // 这样ksize滑动条的范围是0到10,得到1到21的奇数 if(current_ksize <=0) current_ksize =1; if(current_ksize %2 ==0) current_ksize +=1; // 确保为奇数 if(filter_type ==0) { boxFilter(src, dst, -1, Size(current_ksize, current_ksize), Point(-1,-1), true); } else if(filter_type ==1) { blur(src, dst, Size(current_ksize, current_ksize)); } else if(filter_type ==2) { GaussianBlur(src, dst, Size(current_ksize, current_ksize), 0); } imshow("Filter Demo", dst); } 但这样,ksize的滑动条可能范围是0到10,对应1到21的奇数。或者,直接让用户调整ksize的值为奇数,比如滑动条的最小值为1,步长为2。但OpenCV的createTrackbar的步长只能是1,所以可能需要手动处理。 或者,滑动条的值代表核的大小,但用户只能选择奇数。比如,滑动条的值可以是1,3,5...,但用户无法直接输入,只能滑动到奇数值。过这在实现上可能复杂,所以可能更简单的是让滑动条的值代表n,核大小为2n+1,这样可以确保奇数正数。 例如,滑动条的范围是0到10,对应核大小1,3,5,...,21。这样处理起来方便。 但用户可能需要更大的核,比如到31,所以滑动条的最大值可以设置为15,对应核大小31。 现在,代码结构: int main() { src = imread("image.jpg"); if(src.empty()) return -1; namedWindow("Filter Demo"); createTrackbar("Filter Type", "Filter Demo", &filter_type, 2, onTrackbar); createTrackbar("Kernel Size", "Filter Demo", &ksize, 15, onTrackbar); onTrackbar(0,0); // 初始化 waitKey(0); return 0; } 这样,滑动条“Filter Type”从0到2,对应三种滤波类型。“Kernel Size”滑动条从0到15,对应核大小1到31(2*15+1=31)。 在回调函数中,计算current_ksize = 2*ksize +1,确保至少1,并且是奇数。 然后根据filter_type调用对应的滤波函数。 这样应该能满足用户的需求。同时,需要处理图像读取失败的情况,并给出提示。 另外,用户可能需要同时调整高斯滤波的标准差。但问题中没有提到,所以可能暂时忽略,或者在代码中用固定值,如sigma=0,由OpenCV自动计算。 测试代码的正确性,确保三种滤波都能正确应用,并且滑动条调整时图像实时更新。 最后,用户可能需要完整的可编译代码,所以需要包含必要的头文件命名空间。 综上,示例代码的大致结构是: #include <opencv2/opencv.hpp> using namespace cv; Mat src, dst; int filter_type = 0; // 0:Box, 1:Mean, 2:Gaussian int ksize = 0; // 对应核大小为2*ksize+1 void onTrackbar(int, void*) { int current_ksize = 2*ksize +1; if(current_ksize <1) current_ksize =1; if(current_ksize%2 ==0) current_ksize +=1; if(filter_type ==0) { boxFilter(src, dst, -1, Size(current_ksize, current_ksize), Point(-1,-1), true); } else if(filter_type ==1) { blur(src, dst, Size(current_ksize, current_ksize)); } else if(filter_type ==2) { GaussianBlur(src, dst, Size(current_ksize, current_ksize), 0); } imshow("Filter Demo", dst); } int main() { src = imread("test.jpg"); if(src.empty()) { printf("Error loading image\n"); return -1; } namedWindow("Filter Demo"); createTrackbar("Type: 0-Box,1-Mean,2-Gauss", "Filter Demo", &filter_type, 2, onTrackbar); createTrackbar("Kernel Size (n:2n+1)", "Filter Demo", &ksize, 15, onTrackbar); onTrackbar(0,0); waitKey(0); return 0; } 这样应该可以实现用户的需求。需要检查函数参数是否正确,比如boxFilter的ddepth设为-1表示输出与输入相同的深度。高斯滤波的sigmaX设为0,让OpenCV自动计算。 最后,测试代码是否存在问题,例如滑动条变化时是否正确处理核大小,特别是当ksize为0时,current_ksize=1,符合要求。</think>以下是在OpenCV C++中创建滑动条控制三种线性滤波的示例代码,包含方框滤波、均值滤波高斯滤波的动态调节功能: ```cpp #include <opencv2/opencv.hpp> using namespace cv; // 全局变量声明 Mat src, dst; int filter_type = 0; // 0:方框滤波 1:均值滤波 2:高斯滤波 int kernel_size = 1; // 滑动条数值对应实际核尺寸(2n+1) // 回调函数 void applyFilter(int, void*) { int ksize = 2 * kernel_size + 1; // 将滑动条值转换为奇数核尺寸 ksize = MAX(ksize, 1); // 确保最小核尺寸为1 switch(filter_type) { case 0: boxFilter(src, dst, -1, Size(ksize, ksize), Point(-1,-1), true); break; case 1: blur(src, dst, Size(ksize, ksize)); break; case 2: GaussianBlur(src, dst, Size(ksize, ksize), 0); break; } imshow("Filter Controller", dst); } int main() { src = imread("input.jpg"); if(src.empty()) { printf("Error loading image\n"); return -1; } namedWindow("Filter Controller", WINDOW_AUTOSIZE); // 创建控制滤波类型的滑动条 createTrackbar("Filter:\n0-Box\n1-Mean\n2-Gauss", "Filter Controller", &filter_type, 2, applyFilter); // 创建控制核尺寸的滑动条 createTrackbar("Kernel Size", "Filter Controller", &kernel_size, 15, // 对应最大核尺寸31x31 applyFilter); applyFilter(0, 0); // 初始调用 waitKey(0); return 0; } ``` ### 功能说明 1. **滑动条交互**: - 第一个滑动条控制滤波类型:0-方框滤波,1-均值滤波,2-高斯滤波 - 第二个滑动条控制核尺寸(实际尺寸为$2n+1$,保证奇数核) - 调整参数后图像会实时更新 2. **核心函数**: - `boxFilter()`:方框滤波,当`normalize=true`时等效均值滤波[^3] - `blur()`:直接实现均值滤波[^2] - `GaussianBlur()`:高斯滤波,$\sigma$参数设为0时自动计算标准差[^1] 3. **使用注意**: - 核尺寸参数会自动转换为奇数($2n+1$) - 建议核尺寸要超过图像尺寸的1/5 - 高斯滤波的$\sigma$值可根据需求添加额外滑动条控制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值