作者:咕唧咕唧liukun321
来自:http://blog.csdn.net/liukun321
1.图像基本运算分类及理论依据
图像的像素级运算
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=1,b=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); } } }
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)
R为f(x, y)的灰度级。
主要应用举例
获得一个图像的负像
获得一个子图像的补图像
g(x,y) = f(x,y) h(x,y)
主要应用举例
获得相交子图像
g(x,y) = f(x,y) h(x,y)
主要应用举例
求两个子图像的相交子图
说到代数运算,忍不住提一下opencv2:如今的opencv可以说让我们进入了傻瓜编程的阶段。Opencv2不仅为我们提供了更加丰富的算法封装,而且在很多函数的命名和操作上越来越像matlab,极大的简化了操作(比如不用再纠结于内存的释放),我们就能把更多的精力放在算法优化而非冗杂的程序设计上!
对于算术运算这块:Opencv2 Mat为我们重载了很多运算符,大多数的算术运算在opencv2中都有对应的重载操作符。如位操作&、|、^、~;函数min、max、abs;比较操作符< 、<=、 ==等(注意比较操作符返回的是二进制图像)。当然它也重载了很多矩阵运算:矩阵乘法 *。另外通过Mat的成员函数也可以方便的求解矩阵求逆 inv、矩阵转置t()、矩阵行列式等运算。这极大的简化了代码的复杂度。举一个例子,我们要获得一幅图像的负像就可以这样写:
cv::Mat img = imread(“../test.jpg”,0); img = ~img;//取反
总之。。。这部分内容就先到这。。。