opencv学习24-27——伽马矫正,最邻近差值,双线性差值,双三次插值

Q:伽马矫正

伽马矫正是一种针对图像像素值作非线性处理的手段,主要为指数形式变化,目的在于优化图像的视觉效果。一般表达式如下。

Vout=AVin^\alpha

Vout表示输出的像素值,Vin表示输入的像素值,A为系数,阿尔法为指数。标准的表达式如下。

Vout=\frac{1}{c}Vin^{\tfrac{1}{g}}

c为常数,g为伽马变量,两个参数可自定义。代码如下

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
#include"iostream"
#include"math.h"
using namespace cv;
Mat gamacorrect(Mat img,double c,double g)
{
	Mat out=Mat::zeros(img.rows,img.cols,CV_8UC3);
	double V=0;
	for(int i=0;i<img.rows;i++)
	{
		for(int j=0;j<img.cols;j++)
		{
			for(int k=0;k<img.channels();k++)
			{
				V=(double)img.at<Vec3b>(i,j)[k]/255;
				out.at<Vec3b>(i,j)[k]=(uchar)(pow(V/c,1/g)*255);
			}
		}
	}
	return out;
}
void main()
{
	Mat img = imread("ck567.jpg");
	imshow("orignal jpg",img);
	Mat out = gamacorrect(img,1,2.2);
	imshow("gamacorrect",out);
	waitKey(0);
	destroyAllWindows();
}

效果如下

Q:最邻近差值

最邻近差值是图像处理中最简单最快速的图像放大算法,这里简单的叙述它的原理。

如果有一幅图像的大小为x*y,现在要对他进行放大,放大倍数为rx和ry,也就是说最后生成的图像大小为(x*rx)*(y*ry),与原图像相比,就多出了许多的像素值,当然原本的像素值也不再适应,因此整幅图像的像素值都要重新填充。因此就出现了原图像的像素值与放大后的像素值之间的关系,他们之间的关系就通过放大倍数来建立,一般的关系式如下,中括号表示四舍五入。

Vout=V\left [ \frac{x}{rx}, \frac{y}{ry} \right ]

原图像的像素值已知,就根据要求的放大倍数通过四舍五入求得未知像素值。代码如下

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
#include"iostream"
#include"math.h"
using namespace cv;
Mat Nearest_ne_interpolation(Mat img,double rx,double ry)
{
	int height=(int)(img.rows*rx);
	int width=(int)(img.cols*ry);
	Mat out=Mat::zeros(height,width,CV_8UC3);
	int ri,rj;
	for(int i=0;i<height;i++)
	{
		ri=(int)(i/rx+0.5);
		ri=min(ri,img.rows-1);
		for(int j=0;j<width;j++)
		{
			rj=(int)(j/ry+0.5);
			rj=min(rj,img.cols-1);
			for(int k=0;k<img.channels();k++)
			{
				out.at<Vec3b>(i,j)[k]=(uchar)img.at<Vec3b>(ri,rj)[k];
			}
		}
	}
	return out;
}
void main()
{
	Mat img = imread("ck567.jpg");
	imshow("orignal jpg",img);
	Mat out = Nearest_ne_interpolation(img,0.3,0.3);
	imshow("Nearest_ne_interpolation",out);
	waitKey(0);
	destroyAllWindows();
}

效果如下

Q:双线性差值

 最邻近差值虽然速度快计算简单,但是效果却不太好,因此使用较为复杂的双线性差值能得到更好的显示。双线性变换的原理这里引用https://blog.csdn.net/a664607530/article/details/79314019中描述的。

双线性插值算法描述如下:
  对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(i+u,j+v) (其中i、j均为浮点坐标的整数部分,u、v为浮点坐标的小数部分,是取值[0,1)区间的浮点数),则这个像素得值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1) 

其中f(i,j)表示源图像(i,j)处的的像素值,以此类推。

现在假如目标图的象素坐标为(1,1),那么反推得到的对应于源图的坐标是(0.75 , 0.75), 这其实只是一个概念上的虚拟象素,实际在源图中并不存在这样一个象素,那么目标图的象素(1,1)的取值不能够由这个虚拟象素来决定,而只能由源图的这四个象素共同决定:(0,0)(0,1)(1,0)(1,1),而由于(0.75,0.75)离(1,1)要更近一些,那么(1,1)所起的决定作用更大一些,这从公式1中的系数uv=0.75×0.75就可以体现出来,而(0.75,0.75)离(0,0)最远,所以(0,0)所起的决定作用就要小一些,公式中系数为(1-u)(1-v)=0.25×0.25也体现出了这一特点。

代码如下 

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
#include"iostream"
#include"math.h"
using namespace cv;
Mat bilinear_interpolation(Mat img,double rx,double ry)
{
	int height=(int)(img.rows*rx);
	int width=(int)(img.cols*ry);
	Mat out=Mat::zeros(height,width,CV_8UC3);
	int ri,rj;
	double u,v,val;
	for(int i=0;i<height;i++)
	{
		ri=(int)(i/rx);
		ri=min(ri,img.rows-1);
		u=i/rx-ri;
		for(int j=0;j<width;j++)
		{
			rj=(int)(j/ry);
			rj=min(rj,img.cols-1);
			v=j/ry-rj;
			for(int k=0;k<img.channels();k++)
			{
				val=(1-u)*(1-v)*img.at<Vec3b>(ri,rj)[k]+(1-u)*v*img.at<Vec3b>(ri,rj+1)[k]+(1-v)*u*img.at<Vec3b>(ri+1,rj)[k]+u*v*img.at<Vec3b>(ri+1,rj+1)[k];
				out.at<Vec3b>(i,j)[k]=(uchar)val;
			}
		}
	}
	return out;
}
void main()
{
	Mat img = imread("ck567.jpg");
	imshow("orignal jpg",img);
	Mat out = bilinear_interpolation(img,0.3,0.3);
	imshow("bilinear_interpolation",out);
	waitKey(0);
	destroyAllWindows();
}

效果如下

Q:双三次插值

双三次插值是更为精确地一种缩放算法,缺点是计算量大,运行速度慢。

双线性差值是分析四个像素点,双三次插值是分析目标像素周围的十六个点。

原理与双线性差值类似,区别是得到目标像素值的方法不同,前者是四个点,后者是十六个点,双三次插值需要用到bicubic函数,如下所示

我们需要取得函数中的X取值,并带入函数求得W权重,十六个像素点与全中的加权和为目标点的像素值大小。

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
#include"iostream"
#include"math.h"
using namespace cv;
using namespace std;
double getW(double x)
{
	double a=-0.5;
	if(fabs(x)<=1)
		return (a+2)*pow(fabs(x),3)-(a+3)*pow(fabs(x),2)+1;
	else if(fabs(x)<=2)
		return a*pow(fabs(x),3)-5*a*pow(fabs(x),2)+8*a*fabs(x)-4*a;
	else
		return 0;
}
Mat BiCubic_interpolation(Mat img,double rx,double ry)
{
	int height=(int)(img.rows*rx);
	int width=(int)(img.cols*ry);
	Mat out=Mat::zeros(height,width,CV_8UC3);
	int i_b,j_b,x,y;
	double ri,rj,wi,wj,wsum,val;
	for(int i=0;i<height;i++)
	{
		ri=i/ry;
		i_b=(int)ri;//定位原像素
		for(int j=0;j<width;j++)
		{
			rj=j/rx;
			j_b=(int)rj;//定位原像素
			for(int k=0;k<img.channels();k++)
			{
				wsum=0;
				val=0;
				for(int m=0;m<4;m++)
				{
					x=min(i_b+m,img.rows-1);
					wi=getW(fabs(ri-x));
					for(int n=0;n<4;n++)
					{
						y=min(j_b+n,img.cols-1);
						wj=getW(fabs(rj-y));
						wsum+=wi*wj;
						val+=(double)img.at<Vec3b>(x,y)[k]*wi*wj;
						//out.at<Vec3b>(i,j)[k]=(uchar)((int)val);
					}
				}
				val/=wsum;
				val=min(max((int)val,0),255);
				out.at<Vec3b>(i,j)[k]=(uchar)((int)val);
				//cout<<val<<endl;
			}
		}
	}
	return out;
}
void main()
{
	Mat img = imread("ck567.jpg");
	imshow("orignal jpg",img);
	Mat out = BiCubic_interpolation(img,0.8,0.8);
	imshow("BiCubic",out);
	waitKey(0);
	destroyAllWindows();
}

效果如下

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值