学习Opencv2.4.9(三)---图像的基本运算

作者:咕唧咕唧liukun321

来自:http://blog.csdn.net/liukun321


1.图像基本运算分类及理论依据

图像的像素级运算

1)点运算(灰度变换)——线性点运算、非线性点运算、映射表点运算

点运算特点

  •  点运算针对图像中的每一个像素灰度,独立地进行灰度值的改变
  •  输出图像中每个像素点的灰度值,仅取决于相应输入像素点的值
  •  点运算不改变图像内的空间关系
  •  从像素到像素的操作
  •  点运算可完全由灰度变换函数或灰度映射表确定

实例——“对比度增强、对比度拉伸、灰度变换

常用的线性和非线性点运算的的基础变换函数如下图所示:

学习Opencv2.4.9(三)---图像的基本运算_第1张图片

以上可以抽象解释为对于原图像f(x,y),灰度值变换函数T(f(x,y))唯一确定了非几何变换:

g(x,y) = T(f(x,y))

g(x,y)是目标图像

 灰度变换的目的是为了改善画质,使图像的显示效果更加清晰。而对于彩色原图像f(x,y),颜色值变换函数

Tr(f(x,y)); Tg(f(x,y)); Tb(f(x,y));

唯一确定了非几何变换:

gr(x,y) = Tr(f(x,y))

gg(x,y) = Tg(f(x,y))

gb(x,y) = Tb(f(x,y))

  • 线性点运算变换函数及其变换后在图像上的体现:


a=1b=0: 恒等

a<0: 黑白反转

|a|>1: 增加对比度

|a|<1: 减小对比度

b>0: 增加亮度

b<0: 减小亮度

例如:对于取值范围在(0,255)的灰度图,我们可以通过令:

POUT(X,Y) = (-1)*PIN(X,Y) +255 来获得它的负像

void linearityOpr(Mat src,Mat &dst,float slope,float intercept)
{
    int M = 0;
    int N = 0;
    if(src.empty()){
        std::cout<<"Src pic is empty\n"<<std::endl;
        return;
    }
    M = src.rows;
    N = src.cols;
    int j = 0;
    float gray = 0;
    for(int i = 0;i < M;i++){
        for(j = 0; j < N; j++){
            gray = (float)src.at<uchar>(i,j);
            gray = slope*((float)(1+gray)) + intercept;
            dst.at<uchar>(i,j) = saturate_cast<uchar>(gray);
        }
    }
}


  • 对于灰度对数变换的一般表达式为

t = clog(1+s)

由对数变换的曲线特性我们可以知道,对数变换能增强图像中较暗部分的细节,从而可以扩展被压缩的高值图像中较暗的像素。因此它广泛应用于频谱图像的显示。后面的opencv编程实例将向您展示这一特性。

void logTran(Mat src,Mat &dst,double c)
{
    int M = 0;
    int N = 0;
    if(src.empty()){
        std::cout<<"Src pic is empty\n"<<std::endl;
        return;
    }
    M = src.rows;
    N = src.cols;
    int j = 0;
    double gray = 0;
    for(int i = 0;i < M;i++){
        for(j = 0; j < N; j++){
            gray = (double)src.at<uchar>(i,j);
            gray = c*log((double)(1+gray));
            dst.at<uchar>(i,j) = saturate_cast<uchar>(gray);
        }
    }
}


  • gamma变换。Gamma变换有常被称为指数变换或幂变换,是一种常用的非线性变换。它的变换函数如下:

Y = (X + esp)γ

伽马变换可以根据γ值的不同来选择性的增强低灰度或者高灰度区域的对比度。

γ>1时,图像的高灰度区域对比度得到增强。

γ<1时,图像的低灰度区域对比度得到增强。

γ=1时,灰度线性变换,不改变原图像。

注意:gamma变换函数中 X,Y的取值范围[0,1],因此在对图像做gamma变换前要将图像变换到0~1的动态范围。

void gammaTran(Mat src,Mat &dst,double gamma,double comp)
{
    int M = 0;
    int N = 0;
    if(src.empty()){
        std::cout<<"Src pic is empty"<<std::endl;
        return;
    }
    M = src.rows;
    N = src.cols;
    int j = 0;
    double gray = 0;
    for(int i = 0;i < M;i++){
        for(j = 0; j < N; j++){
            gray = (float)src.at<uchar>(i,j);
            gray = pow((gray+comp)/255.0,gamma)*255;
            dst.at<uchar>(i,j) = saturate_cast<uchar>(gray);
        }
    }
}


  • 阈值变换

void cvThreashhold(Mat src,Mat &dst,float t)
{
    threshold(src,dst,255*t,255,THRESH_BINARY);
}


  • 分段线性变换

分段线性变换也叫做灰度线性拉伸,常用的是分三段分线性变换。实现如下:

void contrastStretch(Mat src,Mat &dst,int x1,int x2,int y1,int y2)
{
    int M = 0;
    int N = 0;
    if(src.empty()){
        std::cout<<"Src pic is empty\n"<<std::endl;
        return;
    }
    M = src.rows;
    N = src.cols;
    int j = 0;
    float gray = 0;
    for(int i = 0;i < M;i++){
        for(j = 0; j < N; j++){
            gray = (float)src.at<uchar>(i,j);
            if(gray <= x1){
                gray = (float)(y1/x1)*gray;
            }else if(gray < x2){
                gray = ((float)(y2-y1)/(float)(x2-x1))*(gray-x1) + y1;
            }else if(gray >= x2){
                gray = ((float)(255-y2)/(float)(255-x2))*(gray-x2) + y2;
            }else{
                std::cout<<"Please reset the coordinate((x1,y1)or(x2,y2))\n"<<std::endl;
            }
            dst.at<uchar>(i,j) = saturate_cast<uchar>(gray);
        }
    }
 }


2)代数运算——加法、减法、乘法、除法

  • 加法运算的定义

C(x,y) = A(x,y) + B(x,y)

主要应用举例

去除“叠加性”噪音

生成图像叠加效果

生成图像叠加效果

对于两个图像f(x,y)h(x,y)的均值有:

g(x,y) = 0.5f(x,y) + 0.5h(x,y)

会得到二次曝光的效果。推广这个公式为:

g(x,y) = αf(x,y) + βh(x,y) 其中α+β= 1

我们可以得到各种图像合成的效果,也可以用于两张图片的衔接

void picBlending(Mat src1 ,Mat src2,Mat &dst,double alpha,double beta,double gamma)
{
    if(src1.empty()){
        std::cout<<"Please set the src image"<<std::endl;
    }
    if(src1.rows == src2.rows&& src1.cols == src2.cols){
        dst = src1.clone();
        addWeighted(dst,alpha,src2,beta,gamma,dst);
    }else if(src1.rows >= src2.rows&& src1.cols >= src2.cols){
        Mat ROI ;
        dst = src1.clone();
        ROI= dst(Rect(src1.cols - src2.cols,0,src2.cols,src2.rows));
        addWeighted(ROI,alpha,src2,beta,gamma,ROI);
    }else if(src1.rows <= src2.rows&& src1.cols <= src2.cols){
        Mat ROI ;
        dst = src2.clone();
        ROI= dst(Rect(src2.cols - src1.cols,0,src1.cols,src1.rows));
        addWeighted(ROI,alpha,src1,beta,gamma,ROI);
    } else{
        std::cout<<"Couldn't blend"<<std::endl;
    }
}


  • 减法的定义

C(x,y) = A(x,y) - B(x,y)

主要应用举例

去除不需要的叠加性图案

检测同一场景两幅图像之间的变化

  • 乘法的定义

C(x,y) = A(x,y) × B(x,y)

主要应用举例

图像的局部显示

用二值蒙板图像与原图像做乘法

3)逻辑运算——求反、异或、或、与

  • 求反的定义

g(x,y) = R - f(x,y)

Rf(x, y)的灰度级。

主要应用举例

获得一个图像的负像

获得一个子图像的补图像

  • 异或运算的定义

g(x,y) = f(x,y) h(x,y)

主要应用举例

获得相交子图像

  • 与运算的定义

g(x,y) = f(x,y) h(x,y)

主要应用举例

求两个子图像的相交子图

学习Opencv2.4.9(三)---图像的基本运算_第2张图片

说到代数运算,忍不住提一下opencv2:如今的opencv可以说让我们进入了傻瓜编程的阶段。Opencv2不仅为我们提供了更加丰富的算法封装,而且在很多函数的命名和操作上越来越像matlab,极大的简化了操作(比如不用再纠结于内存的释放),我们就能把更多的精力放在算法优化而非冗杂的程序设计上!

对于算术运算这块:Opencv2 Mat为我们重载了很多运算符,大多数的算术运算在opencv2中都有对应的重载操作符。如位操作&、|、^、~;函数min、max、abs;比较操作符< 、<=、 ==等(注意比较操作符返回的是二进制图像)。当然它也重载了很多矩阵运算:矩阵乘法 *。另外通过Mat的成员函数也可以方便的求解矩阵求逆 inv、矩阵转置t()、矩阵行列式等运算。这极大的简化了代码的复杂度。举一个例子,我们要获得一幅图像的负像就可以这样写:

cv::Mat img = imread(“../test.jpg”,0);
img = ~img;//取反


总之。。。这部分内容就先到这。。。

你可能感兴趣的:(学习Opencv2.4.9(三)---图像的基本运算)