前一阵想改进个图像处理的算法,发现基础太差了,还是要从基础开始,毕竟一口出不成胖子
于是自己学习一下图像处理的基本知识。看到两个最简单的算法 一个是最近邻内插值和双线性插值,决定自己实现以下,顺便也能了解一下数字图像内部的存储结构。
以下为简单整理
by the way,基础知识逐渐明朗的感觉还是不错滴~程序员最大的开心应该就是程序无bug跑通的时候,无论是多简单的程序。
一个Width*Height nChannel个通道的图像 共具有Width * Height 个像素点。每个像素点有nChannel个通道元素。
以3通道为例,3通道为B G R
如下:
如上所示,每一行有效元素共Width*nChannel个元素,实际上,有时候比这个数要多。
为了提高速度,需要按4字节对齐,因此如果每行元素个数不是4的倍数,需要补齐,这个即为平时总提到的widthstep。
基于以上的了解,实现两种算法
1 最近领内插值算法
就是令变换后像素的灰度值等于距它最近的输入像素的灰度值
如上图 按照比例一致 可知道
Y = W / w * y;
X = H / h * x;
则目标图(x, y)处的灰度值与原图(X, Y )处的灰度值一致: f(x, y) = f(X, Y)
代码:
//最近邻内插
void NearestInsert(const char *OriImagePath, char *OutImagePath, int Width, int Height)
{
IplImage * OriImage = cvLoadImage(OriImagePath, 1);
IplImage * OutImage = cvCreateImage(cvSize(Width, Height), 8, 3);
unsigned char *OriImage_c = (unsigned char *)OriImage->imageData;
unsigned char *OutImage_c = (unsigned char *)OutImage->imageData;
double fx = (double)OriImage->height / (double)Height;//height方向上的比值
double fy = (double)OriImage->width / (double)Width;//width方向上的比值
int channel = OriImage->nChannels;//通道数 一般为RGB 3通道
for (int x = 0; x < Height; x++)
{
for(int y = 0; y < Width; y++)
{
for (int k = 0; k widthStep + y * channel + k;
int ori = (int)(fx * x) * OriImage->widthStep + (int)(fy * y) * channel + k;
OutImage_c[out] = OriImage_c[ori];
}
}
}
//test
cvNamedWindow("OriImage",1);
cvShowImage("OriImage",OriImage);
cvWaitKey(0);
cvNamedWindow("NearestInsert",1);
cvShowImage("NearestInsert",OutImage);
cvWaitKey(0);
//cvSaveImage(OutImagePath,OutImage);
cvReleaseImage(&OriImage);
cvReleaseImage(&OutImage);
}
2 双线性插值
在x y两个方向上分别进行一次线性插值,方向先后不影响顺序。
如上图所示
目标图(x, y) 映射到原图是(X + u, Y + v)(计算方法同最邻近插值)。设u与v分别为X + u,Y + v的小数部分。
由于下标都是整数,因此原图其实并不存在该点。
则取其附近四个领域点为(X, Y) (X, Y + 1) (X + 1, Y) (X + 1, Y + 1)
则目标图(x, y)处的值为 f(x , y) = f(X + u, Y + v) =f (X, Y) * (1 - u) * (1 - v) + f(X, Y + 1) * (1 - u) * v + f(X + 1, Y) * u * (1 - v) + f (X + 1, Y + 1) * u * v;
代码如下:
//双线性内插
void BilinearInsert(const char *OriImagePath, char *OutImagePath, int Width, int Height)
{
IplImage * OriImage = cvLoadImage(OriImagePath, 1);
IplImage * OutImage = cvCreateImage(cvSize(Width, Height), 8, 3);
unsigned char *OriImage_c = (unsigned char *)OriImage->imageData;
unsigned char *OutImage_c = (unsigned char *)OutImage->imageData;
double fx = (double)OriImage->height / (double)Height;//height方向上的比值
double fy = (double)OriImage->width / (double)Width;//width方向上的比值
int channel = OriImage->nChannels;//通道数 一般为RGB 3通道
for (int x = 0; x < Height; x++)
{
for(int y = 0; y < Width; y++)
{
//对于目标图像(x, y),实际映射到原图的坐标为(fx * x, fy * y),但是若为小数,原图并不存在该店,因此近似为(i, j)
int i = fx * x;
int j = fy * y;
int out = x * OutImage->widthStep + y * channel;
//找到四个领域的下标
int ori1 = i * OriImage->widthStep + j * channel;
int ori2 = i * OriImage->widthStep + (j + 1) * channel;
int ori3 = (i + 1) * OriImage->widthStep + j * channel;
int ori4 = (i + 1) * OriImage->widthStep + (j + 1) * channel;
//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)
for (int k = 0; k