openCV学习笔记

*********************2016.8.22***********************
@2016.8.9~2016.8.12 OpenCV学习细节总结
1.直接读取图像灰度图的方法:Mat LogoMask = imread(“Logo.jpg”, 0);

2.掩模必须为灰度图

3.获取图像ROI的方法:Mat GirlROI = Girl(Rect(200, 200, Logo.cols, Logo.rows));

4.分离颜色通道时候 须要定义 vector channels ——–> 须要搞懂!先保留问题

5.OpenCV的色彩空间为 BGR

6.亮度对比度调节理论依据:g(i,j)=a*f(i,j)+b(其中a为对比度,b为亮度)

7.OpenCV 源代码中的定义:
typedef Vec <uchar, 2> Vec2b;
Vec2b—表示每个Vec2b对象中,可以存储2个char(字符型)数据
Vec3b—表示每一个Vec3b对象中,可以存储3个char(字符型)数据,比如可以用这样的对象,去存储RGB图像中的
Vec4b—表示每一个Vec4b对象中,可以存储4个字符型数据,可以用这样的类对象去存储—4通道RGB+Alpha的图

8.二维图像的遍历像素方法如下(详情查看Mat::at)

Mat H(100, 100, CV_64F);
for (int i = 0; i < H.rows; i++)
{
    for (int j = 0; j < H.cols; j++)
    {
        H.at<double>(i, j) = 1. / (i + j + 1);
    }
}

9.三通道图像的遍历像素方法如下

for (int y = 0; y < SrcImg.rows; ++y)
{
    for (int x = 0; x < SrcImg.cols; ++x)
    {
        for (int c = 0; c < 3; ++c)
        {
            DstImg.at<Vec3b>(y, x)[c]= saturate_cast<uchar>((ContrastValue*0.01)*SrcImg.at<Vec3b>(y,x)[c]+BrightValue);     
        }
    }
}

10.滤波函数内核的大小不能为0,否则会报错

11.滤波函数内核的大小最好都为奇数。

@2016.8.9~2016.8.12 OpenCV学习函数总结

1.两图像加权融合:addWeighted()

2.分离图像通道:vector channels;
split(Girl, channels);

3.读取BGR中的其中一通道(B为0,G为1,R为2):B=channels.at(0);G=channels.at(1);R=channels.at(2)

4.三通道合成:Merge(channels,OutputDst);

5.运算结果可能超出像素取值范围(溢出),还可能是非整数(如果是浮点数的话),用来确保有效值:saturate_cast<…>()
saturate in the name means that when the input value v is out of the range of the target type
the result is not formed just by taking low bits of the input, but instead the value is clipped.For example :
uchar a = saturate_cast(-100); // a = 0 (UCHAR_MIN)
short b = saturate_cast(33333.33333); // b = 32767 (SHRT_MAX)

6.用于初始化输出图像,使输出图像与输入图像有相同的尺寸类型:
boxFilterDstImg = Mat::zeros(SrcImg.size(), SrcImg.type());
或者
boxFilterDstImg = SrcImg.clone();

7.创建拉动条:createTrackbar()

8.三种线性滤波函数:boxFilter();blur();GaussianBlur();

9.二种非线性滤波函数:medianBlur();bilateralFilter();

10.Returns a structuring element of the specified size and shape for morphological operations(形态学变换用的核):getStructuringElement()

11.
形态学变换初级 dilate(),erode()
形态学变换高级 morphologyEx(MORPH_OPEN /MORPH_CLOSE/ MORPH_GRADIENT/ MORPH_TOPHAT/ MORPH_BLACKHAT)

12.三种边缘检测与一个服务于Sobel 的滤波器
Canny,Sobel,Laplacian/ Scharr滤波器

*********************2016.8.21***********************
@边缘检测的一般步骤

原图->滤波->转化为灰度图->Canny()/Sobel()【其中Scharr滤波器专为Sobel()函数设计】/Laplacian()。

@两个比较重要的函数

转化为灰度图用的函数:cvtColor()
其他类型格式转化为CV_8U用的函数:convertScaleAbs()

@两个等价的函数

Scharr(src, dst, ddepth, dx, dy, scale,delta, borderType);
<<=====>>Sobel(src, dst, ddepth, dx, dy, CV_SCHARR,scale, delta, borderType);

@Canny,Sobel,Laplacian,Scharr用法源代码

bool Canny()
{
    Mat SrcImg = imread("benz.jpg");
    if (!SrcImg.data)
        return false;
    Mat EdgeImg;
    Mat BWImg;
    Mat DstImg;

    cvtColor(SrcImg, BWImg, CV_RGB2GRAY);
    blur(BWImg, BWImg, Size(3, 3));
    //第三个参数,double类型的threshold1,第一个滞后性阈值。
    //第四个参数,double类型的threshold2,第二个滞后性阈值。
    //这个函数阈值1和阈值2两者的小者用于边缘连接,而大者用来控制强边缘的初始段
    //推荐的高低阈值比在2:1到3:1之间。
    Canny(BWImg, EdgeImg, 3, 9);
    //void copyTo(OutputArray m, InputArray mask) const;
    //! copies those matrix elements to "m" that are marked with non-zero mask elements.
    SrcImg.copyTo(DstImg, EdgeImg);
    namedWindow("BWGirl");
    imshow("BWGirl", DstImg);
}
bool Sobel()
{
    Mat SrcImgRGB = imread("benz.jpg");
    if (!SrcImgRGB.data)
        return false;
    Mat DstImgx, DstImgy, DstImg, absDstImgx, absDstImgy;
    Mat SrcImgBW;
    cvtColor(SrcImgRGB, SrcImgBW, CV_RGB2GRAY);
    //Sobel与Scharr滤波器是等价的
    //Scharr(..........)  <==>   Sobel(Src,Dst,CV_16S,1,0,Scharr)
    //ksize – Size of the extended Sobel kernel. It must be 1, 3, 5, or 7.
    //以下默认为Ksize=1
    Sobel(SrcImgBW, DstImgx, CV_8U, 1, 0);
    Sobel(SrcImgBW, DstImgy, CV_8U, 0, 1);
    addWeighted(DstImgx, 0.5, DstImgy, 0.5, 0, DstImg);
    namedWindow("Sobel x方向");
    namedWindow("Sobel y方向");
    namedWindow("Sobel x,y方向通过加权叠加");
    namedWindow("原图");
    imshow("Sobel x方向", DstImgx);
    imshow("Sobel y方向", DstImgy);
    imshow("Sobel x,y方向通过加权叠加", DstImg);
    imshow("原图", SrcImgRGB);
}
//Scharr滤波器,不是算子。
bool Scharr()
{
    Mat SrcImg, GraySrcImg;
    Mat DstImgX, DstImgY;
    Mat AbsDstImgX, AbsDstImgY;
    Mat DstImg;

    SrcImg = imread("benz.jpg");
    if (!SrcImg.data)
        return false;
    //中值滤波
    medianBlur(SrcImg, SrcImg, 3);
    //转化为灰度图
    cvtColor(SrcImg, GraySrcImg, CV_RGB2GRAY);
    //计算x,y方向的导数
    Scharr(GraySrcImg, DstImgX, CV_16S, 1, 0);
    Scharr(GraySrcImg, DstImgY, CV_16S, 0, 1);
    //转化为CV_8U
    convertScaleAbs(DstImgX, AbsDstImgX);
    convertScaleAbs(DstImgY, AbsDstImgY);
    //加权融合
    addWeighted(AbsDstImgX, 0.5, AbsDstImgY, 0.5, 0, DstImg);
    namedWindow("滤波后的图");
    imshow("滤波后的图", DstImg);
}

bool Laplacian()
{
    Mat SrcImg, GraySrcImg, DstImg;
    SrcImg = imread("benz.jpg");
    //先对RGB图像滤波,滤波完后再转化为灰度图。
    GaussianBlur(SrcImg, SrcImg, Size(3, 3), 0);
    cvtColor(SrcImg, GraySrcImg, CV_RGB2GRAY);
    //通过拉普拉斯变换边缘检测函数的核有默认值1,核越大,边缘越明显
    //tips:也可以试试改变输出图像不同深度,再转化为8U,效果是不同的。
    //第三个参数,int类型的ddepth,输出图像的深度,支持如下src.depth()和ddepth的组合:
    //若src.depth() = CV_8U, 取ddepth = -1 / CV_16S / CV_32F / CV_64F
    //若src.depth() = CV_16U / CV_16S, 取ddepth = -1 / CV_32F / CV_64F
    //若src.depth() = CV_32F, 取ddepth = -1 / CV_32F / CV_64F
    //若src.depth() = CV_64F, 取ddepth = -1 / CV_64F
    Laplacian(GraySrcImg, DstImg, CV_16U, 3);
    //进行CV_16U---->CV_8U的函数
    //convertScaleAbs:On each element of the input array, the function convertScaleAbs performs three operations sequentially: 
    //scaling, taking an absolute value, conversion to an unsigned 8-bit type:
    convertScaleAbs(DstImg, DstImg);
    namedWindow("灰度图");
    namedWindow("拉普拉斯变换图");
    imshow("灰度图", GraySrcImg);
    imshow("拉普拉斯变换图", DstImg);
}

@在学习浅墨的OpenCV入门教程(Soble算子)遇到的问题思考
在学习Sobel算子时,浅墨的代码如下:

int main( )  
{  
    //【0】创建 grad_x 和 grad_y 矩阵  
    Mat grad_x, grad_y;  
    Mat abs_grad_x, abs_grad_y,dst;  

    //【1】载入原始图    
    Mat src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  

    //【2】显示原始图   
    imshow("【原始图】sobel边缘检测", src);   

    //【3】求 X方向梯度  
    Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( grad_x, abs_grad_x );  
    imshow("【效果图】 X方向Sobel", abs_grad_x);   

    //【4】求Y方向梯度  
    Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( grad_y, abs_grad_y );  
    imshow("【效果图】Y方向Sobel", abs_grad_y);   

    //【5】合并梯度(近似)  
    addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );  
    imshow("【效果图】整体方向Sobel", dst);   

    waitKey(0);   
    return 0;   
}  

然后我问了自己一个问题:

//【3】求 X方向梯度  
Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT ); 
//【4】求Y方向梯度  
Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );   

为什么目标图像的深度要转化为CV_16S(16位有符号)类型呢?
接下去发现,浅墨的代码在后续又将其转化成为8位无符号,代码如下:

convertScaleAbs( grad_x, abs_grad_x ); 

convertScaleAbs( grad_y, abs_grad_y );   

通过了查询帮助文档了解convertScaleAbs这个函数:

On each element of the input array, the function convertScaleAbs performs three operations sequentially: scaling, taking an absolute value, conversion to an unsigned 8-bit type。

那么我直接在Sobel函数中填入CV_8U的参数呢????!!!!

于是,有了下面这两张图片的对比:其中第一张为直接在Sobel函数里面填入CV_8U,第二张为Sobel输出CV_16S后通过convertScaleAbs()转化为CV_8U.仔细看还是有那么一点差别的(ps:原图为RGB,没有转化为灰度图像,不知道会不会这个因素有影响。。。)
这里写图片描述

通过cvtColor(Src,Dst,CV_RGB2GRAY)将原图转为灰度图后再看效果:
这里写图片描述

嗯,还是有差别的!!!!

@膨胀/腐蚀,开运算/闭运算,顶帽/黑帽综合代码

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;

//全局变量
Mat SrcImg;
Mat DstImg;
/*源代码:enum { MORPH_RECT=0, MORPH_CROSS=1, MORPH_ELLIPSE=2 };*/
int ElementShape = MORPH_RECT;
//10为分界点,超过10与小于10各对应两种状态
int DilateErodeValue = 11;
int OpenCloseValue = 11;
int TopBlackHatValue = 11;
//回调函数声明
void CallbackDilateErode(int, void*);
void CallbackOpenClose(int, void*);
void CallbackTopBlackHat(int, void*);
//打印在DOS界面的帮助信息
void PrintHelpText();
//主函数
int main()
{
    SrcImg = imread("Logo.jpg");
    if (!SrcImg.data)
        return false;

    PrintHelpText();
    DstImg = SrcImg.clone();
    //命名窗口一定要先创建
    //之前在回调函数里面才创建命名窗口,导致Trackbar显示不出来
    namedWindow("膨胀腐蚀效果图");
    namedWindow("开闭运算效果图");
    namedWindow("顶黑帽运算效果图");

    createTrackbar("腐蚀与膨胀", "膨胀腐蚀效果图", &DilateErodeValue, 20, CallbackDilateErode);
    createTrackbar("开闭运算", "开闭运算效果图", &OpenCloseValue, 20, CallbackOpenClose);
    createTrackbar("顶黑帽运算", "顶黑帽运算效果图", &TopBlackHatValue, 20, CallbackTopBlackHat);

    namedWindow("原图");
    imshow("原图", SrcImg);
    //轮询获取按键信息 
    while (1)
    {
        int getKeyInput = waitKey(0);//获取按键的ASCII码
        //Trackbar改变的时候调用回调函数
        CallbackDilateErode(DilateErodeValue, 0);
        CallbackOpenClose(OpenCloseValue, 0);
        CallbackTopBlackHat(TopBlackHatValue, 0);
        //根据按键的输入,改变核的形状
        if (27 == (char)getKeyInput)//ESC
        {
            break;
        }
        else if ('A'==(char)getKeyInput)
        {
            ElementShape = MORPH_RECT;
        }
        else if ('S' == (char)getKeyInput)
        {
            ElementShape = MORPH_ELLIPSE;
        }
        else if ('D' == (char)getKeyInput)
        {
            ElementShape = MORPH_CROSS;
        }
        else if(' '== (char)getKeyInput)//空格
        {
            ElementShape = (ElementShape + 1) % 3;//三种状态顺序切换
        }
    }

    waitKey();
    return true;
}
/*回调函数中的Offset表示偏移的中心点*/

void CallbackDilateErode(int ,void*)
{
    int Offset = 10;
    int AbsOffset = (DilateErodeValue - Offset) > 0 ? (DilateErodeValue - Offset) : -(DilateErodeValue - Offset);
    Mat element=getStructuringElement(ElementShape, Size(AbsOffset * 2 + 1, AbsOffset * 2 + 1));
    if (DilateErodeValue > 10)//腐蚀
    {
        morphologyEx(SrcImg, DstImg, MORPH_ERODE, element);
    }
    else//膨胀
    {
        morphologyEx(SrcImg, DstImg, MORPH_DILATE, element);
    }
    imshow("膨胀腐蚀效果图", DstImg);
}

void CallbackOpenClose(int, void*)
{
    int Offset = 10;
    int AbsOffset = (OpenCloseValue - Offset) > 0 ? (OpenCloseValue - Offset) : -(OpenCloseValue - Offset);
    Mat element = getStructuringElement(ElementShape, Size(AbsOffset * 2 + 1, AbsOffset * 2 + 1));
    if (OpenCloseValue > 10)//闭运算
    {
        morphologyEx(SrcImg, DstImg, MORPH_CLOSE, element);
    }
    else//开运算
    {
        morphologyEx(SrcImg, DstImg, MORPH_OPEN, element);
    }
    imshow("开闭运算效果图", DstImg);
}

void CallbackTopBlackHat(int, void*)
{
    int Offset = 10;
    int AbsOffset = (TopBlackHatValue - Offset) > 0 ? (TopBlackHatValue - Offset) : -(TopBlackHatValue - Offset);
    Mat element = getStructuringElement(ElementShape, Size(AbsOffset * 2 + 1, AbsOffset * 2 + 1));
    if (TopBlackHatValue > 10)//黑帽运算
    {
        morphologyEx(SrcImg, DstImg, MORPH_BLACKHAT, element);
    }
    else//闭帽运算
    {
        morphologyEx(SrcImg, DstImg, MORPH_TOPHAT, element);
    }

    imshow("顶黑帽运算效果图", DstImg);
}
//DOS界面下显示帮助信息
void PrintHelpText()
{
    printf( "\t\t\t\t\  帮助信息"
                "\n\t 按下按键ESC退出"
                "\n\t  按下A------->掩膜形状为MORPH_RECT"
                "\n\t  按下S------->掩膜形状为MORPH_ELLOPSE"
                "\n\t  按下D------->掩膜形状为MORPH_CROSS"
                "\n\t  按下SPACE------->掩膜形状为三种顺序切换");
}

*********************2016.8.20***********************
@数学形态学

数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换、灰值腐蚀和膨胀、灰值开闭运算、灰值形态学梯度等。

开运算:Dst=Open(Src,element)=dilate(erode(Src,element));
闭运算:Dst=Close(Src,element)=erode(dilate(Src,element));

简单来讲,形态学操作就是基于形状的一系列图像处理操作。OpenCV为进行图像的形态学变换提供了快捷、方便的函数。最基本的形态学操作有二种,他们是:膨胀与腐蚀(Dilation与Erosion)。

在进行腐蚀和膨胀的讲解之前,首先需要注意,腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。

@openCV练习中图像读取不出来的问题

有时候图像读取失败可以试卷改 DEBUG 或者 RELEASE 模式

*********************2016.8.19***********************
@感觉双边滤波更接近于磨皮的效果。
@中值滤波与均值滤波器比较
中值滤波器与均值滤波器比较的优势:在均值滤波器中,由于噪声成分被放入平均计算中,所以输出受到了噪声的影响,但是在中值滤波器中,由于噪声成分很难选上,所以几乎不会影响到输出。因此同样用3x3区域进行处理,中值滤波消除的噪声能力更胜一筹。中值滤波无论是在消除噪声还是保存边缘方面都是一个不错的方法。
中值滤波器与均值滤波器比较的劣势:中值滤波花费的时间是均值滤波的5倍以上。

中值滤波在一定条件下,可以克服线性滤波器(如均值滤波等)所带来的图像细节模糊,如下图:
中值滤波在一定条件下,可以克服线性滤波器(如均值滤波等)所带来的图像细节模糊

@方框滤波&均值滤波&高斯滤波&中值滤波&双边滤波 源代码

#include<iostream>
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>

using namespace cv;
using namespace std;
//main函数与回调函数都要使用到-->所以定义成全局变量
Mat SrcImg;//输入图像
Mat boxFilterDstImg;//方框滤波输出图像
Mat blurDstImg;//均值滤波输出图像
Mat GaussianBlurDstImg;//高斯滤波输出图像
Mat meddianBlurDstImg;//中值滤波输出图像
Mat bilateralBlurDstImg;//双边滤波输出图像
//设定滤波函数的内核大小的初值
int boxFilterValue = 2;
int blurValue = 2;
int GaussianBlurValue = 2;
int meddianBlurValue = 7;
int bilateralBulrValue = 25;
//回调函数的声明
void CallbackBoxFilter(int, void*);
void CallbackBlur(int, void*);
void CallbackGaussianBlur(int, void*);
void CallbackMeddianBlur(int, void*);
void CallbackBilateralBlur(int, void*);
//主函数
int main()
{
    //DOS界面颜色
    system("color 1C");
    SrcImg = imread("Dragon.jpg");
    if (!SrcImg.data)
    {
        printf("读取图片失败");
        return false;
    }
    //用于初始化输出图像,使输出图像与输入图像有相同的尺寸类型
    //但是好像不用初始化也可以
    //boxFilterDstImg = Mat::zeros(SrcImg.size(), SrcImg.type());
    //blurDstImg= Mat::zeros(SrcImg.size(), SrcImg.type());
    //GaussianBlurDstImg = Mat::zeros(SrcImg.size(), SrcImg.type());
    //也可以用clone函数来实现输出图像与输入图像有相同的尺寸类型
    boxFilterDstImg = SrcImg.clone();
    blurDstImg= SrcImg.clone();
    GaussianBlurDstImg= SrcImg.clone();
    meddianBlurDstImg= SrcImg.clone();
    bilateralBlurDstImg= SrcImg.clone();
    //创建调用回调函数的Trackbar函数
    createTrackbar("方框滤波", "方框滤波效果图", &boxFilterValue, 20, CallbackBoxFilter);
    createTrackbar("均值滤波", "均值滤波效果图", &blurValue, 20, CallbackBlur);
    createTrackbar("高斯滤波", "高斯滤波效果图", &GaussianBlurValue, 20, CallbackGaussianBlur);
    createTrackbar("中值滤波", "中值滤波效果图", &meddianBlurValue, 35, CallbackMeddianBlur);
    createTrackbar("双边滤波", "双边滤波效果图", &bilateralBulrValue, 100, CallbackBilateralBlur);
    //Trackbar发生变化时调用的回调函数
    CallbackBoxFilter(boxFilterValue, 0);
    CallbackBlur(blurValue, 0);
    CallbackGaussianBlur(GaussianBlurValue, 0);
    CallbackMeddianBlur(meddianBlurValue, 0);
    CallbackBilateralBlur(bilateralBulrValue, 0);
    //显示原图
    namedWindow("原图");
    imshow("原图", SrcImg);

    //输出一些帮助信息
    cout <<endl<< "\t\t\tby我不是斗哥";

    waitKey();
    return true;
}
//========================================================================
//滤波函数内核的大小不能为0,否则会报错
//所以Size(m,n)里面的数值都+1;
//高斯函数的内核m,n只能为奇数
//========================================================================
//方框滤波回调函数
void CallbackBoxFilter(int, void*)
{
    boxFilter(SrcImg, boxFilterDstImg, -1, Size(boxFilterValue+1, boxFilterValue+1));//方框滤波函数
    namedWindow("方框滤波效果图");
    imshow("方框滤波效果图", boxFilterDstImg);
}
//均值滤波回调函数
void CallbackBlur(int, void*)
{
    blur(SrcImg, blurDstImg, Size(blurValue + 1, blurValue + 1));//均值滤波函数
    namedWindow("均值滤波效果图");
    imshow("均值滤波效果图", blurDstImg);
}
//高斯滤波回调函数
void CallbackGaussianBlur(int, void*)
{
    GaussianBlur(SrcImg, GaussianBlurDstImg, Size(GaussianBlurValue *2+ 1, GaussianBlurValue*2 + 1), 0, 0);//高斯滤波函数
    namedWindow("高斯滤波效果图");
    imshow("高斯滤波效果图",GaussianBlurDstImg);
}
//中值滤波回调函数
void CallbackMeddianBlur(int, void*)
{
    medianBlur(SrcImg, meddianBlurDstImg, meddianBlurValue*2+1);
    namedWindow("中值滤波效果图");
    imshow("中值滤波效果图", meddianBlurDstImg);
}
//双边滤波回调函数
void CallbackBilateralBlur(int, void*)
{
    //参数三:int d   表示在过滤过程中每个像素邻域的直径
    //参数四:double sigmaColor             颜色空间滤波器的sigma值。
    //参数四:这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
    //参数五:double类型的sigmaSpace           坐标空间中滤波器的sigma值,坐标空间的标注方差。
    //参数五:他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。
    //参数五:当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
    bilateralFilter(SrcImg, bilateralBlurDstImg, bilateralBulrValue, bilateralBulrValue * 2, bilateralBulrValue / 2);
    namedWindow("双边滤波效果图");
    imshow("双边滤波效果图", bilateralBlurDstImg);
}

*********************2016.8.18***********************
@滤波:线性滤波与非线性滤波
线性滤波:
方框滤波——boxblur函数
均值滤波(邻域平均滤波)——blur函数
高斯滤波——GaussianBlur函数

非线性滤波:
中值滤波——medianBlur函数
双边滤波——bilateralFilter函数

@常见的滤波器
允许低频率通过的低通滤波器。
允许高频率通过的高通滤波器。
允许一定范围频率通过的带通滤波器。
阻止一定范围频率通过并且允许其它频率通过的带阻滤波器。
允许所有频率通过、仅仅改变相位关系的全通滤波器。

@值得注意的
滤波≠模糊!!!!!
滤波可分低通滤波和高通滤波两种。而高斯滤波是指用高斯函数作为滤波函数的滤波操作,至于是不是模糊,要看是高斯低通还是高斯高通,低通就是模糊,高通就是锐化。
其实说白了是很简单的,对吧:
高斯滤波是指用高斯函数作为滤波函数的滤波操作。
高斯模糊就是高斯低通滤波。

@图像高低频概念
图像的高低频是对图像各个位置之间强度变化的一种度量方法.
低频分量:主要对整副图像的强度的综合度量.
高频分量:主要是对图像边缘和轮廓的度量.
变化越尖锐的地方高频频谱越多,图像细节就是变化尖锐的地方
高反差也一样,它变化很快,过渡区很小,相当于变化尖锐。
深灰到白的颜色变化比浅灰到白要大,颜色过渡更尖锐,高频分量也更多。

@图像深度
我们把计算机存储单个像素点所用到的bit为称之为图像的深度.一般图片是8bit(位)的,则深度是8.

@基本单位不要混
8bit=1Byte=1B
bit->Byte(B)->KB->MB->GB.
*********************2016.8.17***********************
@关于openCV中用createTrackbar调亮度对比度源代码

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
//#include<opencv2\imgproc\imgproc.hpp>
#include<iostream>
using namespace std;
using namespace cv;
//全局变量,因为回调函数和主函数都要用到。
Mat SrcImg;
Mat DstImg;
int ContrastValue = 50;//对比度初值
int BrightValue = 50;//亮度初值
//声明回调函数
void CallbackContrastBright(int, void*);
//主函数
int main(int *argc, char *argv[])
{
    SrcImg = imread("FIFA.png");
    if (!SrcImg.data)
    {
        printf("读取图片失败!请尝试检查!\n"); 
        return false;
    }
    //初始化目标图像
    DstImg = Mat::zeros(SrcImg.size(), SrcImg.type());

    //调用回调函数
    //第一个参数为目标数值,第二个参数因为目标数值为全局变量,所以为0.
    CallbackContrastBright(ContrastValue, 0);
    CallbackContrastBright(BrightValue, 0);

    //createTrackbar(Trackbar名称,Trackbar附着的窗口名称,目标数值的初值,目标数值的最大值,回调函数)
    //createTrackbar 的最后一个参数默认为0,因为目标数值(ContrastValue&BrightValue)为全局变量。
    //定义的回调函数CallbackContrastBright必须为void Foo(int,void*)格式
    createTrackbar("对比度", "效果图", &ContrastValue, 200, CallbackContrastBright);
    createTrackbar("亮度", "效果图", &BrightValue, 100, CallbackContrastBright);

    namedWindow("原图", WINDOW_AUTOSIZE);
    imshow("原图", SrcImg);

    waitKey();
    return true;
}

//定义的回调函数CallbackContrastBright必须为void Foo(int,void*)格式
//typedef Vec<uchar, 2>  Vec2b;
//Vec2b---表示每个Vec2b对象中,可以存储2个char(字符型)数据
//Vec3b---表示每一个Vec3b对象中,可以存储3个char(字符型)数据,比如可以用这样的对象,去存储RGB图像中的
//Vec4b---表示每一个Vec4b对象中,可以存储4个字符型数据,可以用这样的类对象去存储---4通道RGB+Alpha的图
//二维图像的遍历像素方法如下(详情查看Mat::at)
//Mat H(100, 100, CV_64F);
//  for (int i = 0; i < H.rows; i++)
//      for (int j = 0; j < H.cols; j++)
//          H.at<double>(i, j) = 1. / (i + j + 1);
void  CallbackContrastBright(int,void*)
{
    //利用3个循环遍历图像中的所有像素
    for (int y = 0; y < SrcImg.rows; ++y)
    {
        for (int x = 0; x < SrcImg.cols; ++x)
        {
            for (int c = 0; c < 3; ++c)
            {
                //亮度对比度调节理论依据:g(i,j)=a*f(i,j)+b(其中a为对比度,b为亮度)
                //我们的轨迹条一般取值都会整数
                //所以在这里我们可以,将其代表对比度值的ContrastValue参数设为0到200之间的整型,在最后的式子中乘以一个0.01
                //因为我们的运算结果可能超出像素取值范围(溢出),还可能是非整数(如果是浮点数的话)
                //所以我们要用saturate_cast对结果进行转换,以确保它为有效值。
                //saturate in the name means that when the input value v is out of the range of the target type
                //the result is not formed just by taking low bits of the input, but instead the value is clipped.For example :
                //uchar a = saturate_cast<uchar>(-100); // a = 0 (UCHAR_MIN)
                //short b = saturate_cast<short>(33333.33333); // b = 32767 (SHRT_MAX)
                DstImg.at<Vec3b>(y, x)[c]= saturate_cast<uchar>((ContrastValue*0.01)*SrcImg.at<Vec3b>(y,x)[c]+BrightValue);
            }
        }
    }
    namedWindow("效果图", WINDOW_AUTOSIZE);
    imshow("效果图", DstImg);
}

@关于Vec3b,Vec2b,Vec2s等的概念及用法

概念:typedef Vec<unchar,2> Vec2b;

【1】Vec2b—表示每个Vec2b对象中,可以存储2个char(字符型)数据
【2】Vec3b—表示每一个Vec3b对象中,可以存储3个char(字符型)数据,比如可以用这样的对象,去存储RGB图像中的
【3】Vec4b—表示每一个Vec4b对象中,可以存储4个字符型数据,可以用这样的类对象去存储—4通道RGB+Alpha的图

用法:例如遍历一图像:

DstImg.at<Vec3b>(y, x)[c]= saturate_cast<uchar>((ContrastValue*0.01)*SrcImg.at<Vec3b>(y,x)[c]+BrightValue);

`
*********************2016.8.16***********************

@assertion failed错误

在debug模式下报assertion failed错误,请教是什么原因,在return true出现断言,release可以运行?

解决方法:这种情况下一般都是项目属性debug和release的都配置在一块了,debug模式下只要debug的库,release模式下只要release的库,应该就可以

*********************2016.8.12***********************
Mat::Mat(……..)的众多构造函数有很多,很多都涉及到类型 type。type可以是 CV_8UC1,CV_16SC1,…,
CV_64FC4 等。里面的 8U 表示 8 位无符号整数, 16S 表示 16 位有符号整数, 64F
表示 64 位浮点数(即 double 类型); C 后面的数表示通道数,例如 C1 表示一个
通道的图像, C4 表示 4 个通道的图像,以此类推
*********************2016.8.10***********************
@最简单的OpenCV程序,读取与显示图片

using namespace cv;
int main()
{
 Mat pic = imread("fengjing.jpg");
 namedWindow("风景", WINDOW_NORMAL);
 imshow("风景",pic);
 waitKey(10000);
 return 0;
}

*********************2016.8.9***********************
@opencv环境配置问题:一直有未经处理的中断异常

其实,在显示了文件的扩展名后才发现问题:本来是1.jpg的图片结果成了1.jpg.jpg了,因此,在出现上述异常时,不仅仅要考虑到是配置问题,还有可能仅仅就是扩展名没有填写正确。当然,保险的做法是不要隐藏电脑的文件扩展名!!!

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
#图像梯度 (注意都需要cv.convertScaleAbs将得到的有些负值取绝对值得到正数,并将数据转化到0-255之间,且sobel与Scarr算法中的数据位数都是32位浮点型的) import cv2 as cv import numpy as np def sobel_demo(image): #注意是32位float数据 grad_x = cv.Scharr(image, cv.CV_32F, 1, 0) grad_y = cv.Scharr(image, cv.CV_32F, 0, 1) #当用sobel算子不能很好的得到边缘的时候,就可以用Scharr算子,这是加强版的sobel算子,就可以得到 #原图像不是很明显的边缘了 # grad_x =cv.Sobel(image,cv.CV_32F,1,0) # grad_y =cv.Sobel(image,cv.CV_32F,0,1) gradx =cv.convertScaleAbs(grad_x) grady = cv.convertScaleAbs(grad_y) #cv.imshow("gradx",gradx) #cv.imshow("grady",grady) dst = cv.addWeighted(gradx,0.5,grady,0.5,0) cv.imshow("sobel_demo",dst) def lapalace_demo(image): #dst =cv.Laplacian(image,cv.CV_32F) #dst =cv.convertScaleAbs(dst) 会把dst变成单通道的8位的0-255的图像 #也可以用filter2D来做拉普拉斯算子 kernel = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]]) dst = cv.filter2D(image,cv.CV_32F,kernel) dst = cv.convertScaleAbs(dst) cv.imshow("lapalace",dst) src = cv.imread("E:/opencv/picture/step.jpg") cv.imshow("inital_window",src) #sobel_demo(src) lapalace_demo(src) cv.waitKey(0) cv.destroyAllWindows() 分析: 图像梯度可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导。 一、 Sobel算子是普通一阶差分,是基于寻找梯度强度。拉普拉斯算子(二阶差分)是基于过零点检测。通过计算梯度,设置阀值,得到边缘图像。 def sobel_demo(image): #注意是32位float数据 grad_x = cv.Scharr(image, cv.CV_32F, 1, 0) grad_y = cv.Scharr(image, cv.CV_32F, 0, 1) #当用sobel算子不能很好的得到边缘的时候,就可以用Scharr算子,这是加强版的sobel算子,就可以得到 #原图像不是很明显的边缘了 # grad_x =cv.Sobel(image,cv.CV_32F,1,0) # grad_y =cv.Sobel(image,cv.CV_32F,0,1) gradx = cv.convertScaleAbs(grad_x) grady = cv.convertScaleAbs(grad_y) #cv.imshow("gradx",gradx) #cv.imshow("grady",grady) dst = cv.addWeighted(gradx,0.5,grady,0.5,0) cv.imshow("sobel_demo",dst) 1.Sobel算子用来计算图像灰度函数的近似梯度。Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。 2.Sobel具有平滑和微分的功效。即:Sobel算子先将图像横向或纵向平滑,然后再纵向或横向差分,得到的结果是平滑后的差分结果。 Ope

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值