数字图像处理学习笔记(1.1)---位图的读写、几何变换、傅里叶变换、直方图均衡
图像的几何变换
#include"bmp.h" #include<cmath> #include<cstring> #include<cstdio> #include<iostream> using namespace std; void Bitmap::translation(int offsetX, int offsetY)//平移变换 { if (dataBuf == NULL) cout << "请确保已经读入图像数据!" << endl; int w, h;//长宽的循环变量 int k;//像素通道的循环变量 int pixelByte = bitCount / 8;//每像素的字节数 int lineByte = (width_p*bitCount / 8 + 3) / 4 * 4;//每行像素的字节数 unsigned char* transBuf = new unsigned char[height_p*lineByte];//分配数据存储空间 memset(transBuf, 0, height_p*lineByte);//置为黑色 //平移运算 for (h = 0; h < height_p; h++) { for (w = 0; w < width_p; w++) { //输出的点在输入的点范围内 if (h - offsetY >= 0 && h - offsetY <= height_p &&w - offsetX >= 0 && w - offsetX <= width_p) { for (k = 0; k < pixelByte; k++) { *(transBuf + h*lineByte + w*pixelByte + k) = *(dataBuf + (h - offsetY)*lineByte + (w - offsetX)*pixelByte + k); } } } } delete[] dataBuf; dataBuf = transBuf;//释放原数据内存,指向变换后的图像数据 cout << "平移成功!" << endl; } void Bitmap::zoom(float ratioX, float ratioY, char interpolationWay = 'n')//缩放变换 { switch (interpolationWay) { case 'n': neighborInterpolation(ratioX, ratioX); break; case 'l': dbLinearInterpolatin(ratioX, ratioY); break; case 'c': cubicConvoInterpolatin(ratioX, ratioY); break; default: break; } } void Bitmap::neighborInterpolation(float ratioX, float ratioY) { int pixelByte = bitCount / 8;//每像素字节数 int widthOut = int(ratioX*width_p);//输出图像宽度和高度 int heightOut = int(ratioY*height_p); int lineByte = (width_p*bitCount / 8 + 3) / 4 * 4;//原图像每行像素的字节数 int lineByteOut = (widthOut*bitCount / 8 + 3) / 4 * 4;//变换后图像每行像素的字节数 unsigned char* transBuf = new unsigned char[lineByteOut*heightOut]; memset(transBuf, 0, lineByteOut*heightOut); int w, h; int k; //输出图像对应在输入图像中待插值的位置坐标 int coordinateX, coordinateY; for (h = 0; h < heightOut; h++) { for (w = 0; w < widthOut; w++) { //输出图像(w,h)处像素映射到原图像中的坐标值,即插值坐标 coordinateX = int(w / ratioX + 0.5); coordinateY = int(h / ratioY + 0.5); //若插值位置在输入图像范围内,则近邻插值 if (coordinateX >= 0 && coordinateX <= width_p &&coordinateY >= 0 && coordinateY <= height_p) { for (k = 0; k < pixelByte; k++) { *(transBuf + h*lineByteOut + w*pixelByte + k) = *(dataBuf + coordinateY*lineByte + coordinateX*pixelByte + k); } } } } delete[] dataBuf; dataBuf = transBuf; } void Bitmap::dbLinearInterpolatin(float ratioX, float ratioY) { int pixelByte = bitCount / 8;//每像素字节数 int widthOut = int(ratioX*width_p);//输出图像宽度和高度 int heightOut = int(ratioY*height_p); int lineByte = (width_p*bitCount / 8 + 3) / 4 * 4;//原图像每行像素的字节数 int lineByteOut = (widthOut*bitCount / 8 + 3) / 4 * 4;//变换后图像每行像素的字节数 unsigned char* transBuf = new unsigned char[lineByteOut*heightOut]; int w, h; int k; //输出图像对应在输入图像中待插值的位置坐标 float coordinateX, coordinateY; //临时变量,待插值位置向下取整的坐标 int Iu, Iv; //数组,存放插值位置周围的4个像素 unsigned char array[2][2]; //双线性插值 for (h = 0; h< heightOut; h++) { for (w = 0; w< widthOut; w++) { //输出图像坐标为(j,i)的像素映射到原图中的坐标值,即插值位置 coordinateX = w / ratioX; coordinateY = h / ratioY; //对插值位置坐标取整 Iu = (int)coordinateX; Iv = (int)coordinateY; //若插值位置在输入图像范围内,则双线性插值 if (0 <= coordinateX&&coordinateX<width_p && coordinateY >= 0 && coordinateY<height_p) { //将图像每个像素通道的数据进行分别插值, //彩图pixelByte为3,灰度图像pixelByte为1 for (k = 0; k<pixelByte; k++){ //将第k个通道的四个像素数据拷贝至array数组中 array[0][0] = *(dataBuf + Iv*lineByte + Iu*pixelByte + k); array[0][1] = *(dataBuf + Iv*lineByte + (Iu + 1)*pixelByte + k); array[1][0] = *(dataBuf + (Iv + 1)*lineByte + Iu*pixelByte + k); array[1][1] = *(dataBuf + (Iv + 1)*lineByte + (Iu + 1)*pixelByte + k); //两个中间变量 int t1, t2; float ypos, xpos; ypos = coordinateY - Iv; xpos = coordinateX - Iu; //先垂直方向线性插值 t1 = (1 - ypos)*array[0][0] + ypos*array[1][0]; t2 = (1 - ypos)*array[0][1] + ypos*array[1][1]; //再水平方向线性插值 float t = (int)((1 - xpos)*t1 + xpos*t2); //若插值结果小于0,则输出0 if (t<0) t = 0; //若插值结果大于255,则输出255 if (t>255) t = 255; //调用双线性插值函数插值并输出到transBuf中 *(transBuf + h * lineByteOut + w*pixelByte + k) = t; } } else{//边缘像素采用近邻插值 for (k = 0; k<pixelByte; k++) *(transBuf + h * lineByteOut + w*pixelByte + k) = *(dataBuf + Iv*lineByte + Iu*pixelByte + k); } } } delete[] dataBuf; dataBuf = transBuf; } void Bitmap::cubicConvoInterpolatin(float ratioX, float ratioY) { int pixelByte = bitCount / 8;//每像素字节数 int widthOut = int(ratioX*width_p);//输出图像宽度和高度 int heightOut = int(ratioY*height_p); int lineByte = (width_p*bitCount / 8 + 3) / 4 * 4;//原图像每行像素的字节数 int lineByteOut = (widthOut*bitCount / 8 + 3) / 4 * 4;//变换后图像每行像素的字节数 unsigned char* transBuf = new unsigned char[lineByteOut*heightOut]; int w, h; int k; //输出图像对应在输入图像中待插值的位置坐标 float coordinateX, coordinateY; //临时变量,待插值位置向下取整的坐标 int Iu, Iv; //数组,存放插值位置周围16个像素 unsigned char array[4][4]; //循环变量,遍历待插值位置4x4的图像数据 int x, y; //立方卷积插值 for (h = 0; h< heightOut; h++){ for (w = 0; w< widthOut; w++){ //输出图像坐标为(j,i)的像素映射到原图中的坐标值,即插值位置 coordinateX = w / ratioX; coordinateY = h / ratioY; //对插值位置坐标取整 Iu = (int)coordinateX; Iv = (int)coordinateY; //若插值位置在输入图像范围内,则立方卷积插值 if (1 <= coordinateX&&coordinateX<width_p - 2 && coordinateY >= 1 && coordinateY<height_p - 2){ //将图像每个像素通道的数据进行分别插值, //彩图pixelByte为3,灰度图像pixelByte为1 for (k = 0; k<pixelByte; k++){ //将第k个通道的4x4个像素数据拷贝至array数组中 for (y = Iv - 1; y<Iv + 3; y++) { for (x = Iu - 1; x<Iu + 3; x++) { array[y - Iv + 1][x - Iu + 1] = *(dataBuf + y*lineByte + x*pixelByte + k); } } float xpos = coordinateX - Iu; float ypos = coordinateY - Iv; //申请数组,计算插值所需要的系数 float col[4], row[4]; //准备插值的x方向数据源 col[0] = xpos + 1; col[1] = xpos; col[2] = 1 - xpos; col[3] = 2 - xpos; //准备插值的y方向数据源 row[0] = ypos + 1; row[1] = ypos; row[2] = 1 - ypos; row[3] = 2 - ypos; //循环变量 int i; //临时变量 float t; //对水平方向系数数组进行计算 for (i = 0; i<4; i++){ t = fabs(col[i]); if (t >= 0 && t<1) col[i] = pow(t, 3) - 2 * pow(t, 2) + 1; else if (t >= 1 && t<2) col[i] = -pow(t, 3) + 5 * pow(t, 2) - 8 * t + 4; else col[i] = 0; } //对垂直方向系数数组进行计算 for (i = 0; i<4; i++){ t = fabs(row[i]); if (t >= 0 && t<1) row[i] = pow(t, 3) - 2 * pow(t, 2) + 1; else if (t >= 1 && t<2) row[i] = -pow(t, 3) + 5 * pow(t, 2) - 8 * t + 4; else row[i] = 0; } //将计算好的系数与对应图像数据数组作卷积 float tempArray[4], temp; //先x方向卷积 for (i = 0; i<4; i++) tempArray[i] = row[0] * array[0][i] + row[1] * array[1][i] + row[2] * array[2][i] + row[3] * array[3][i]; //再y方向卷积 temp = 0; for (i = 0; i<4; i++) temp += tempArray[i] * col[i]; //将插值结果在图像灰度级范围内输出 if (temp>255) temp = 255; if (temp<0) temp = 0; //调用立方卷积插值函数插值并输出到transBuf中 *(transBuf + h * lineByteOut + w*pixelByte + k) = (unsigned char)temp; } } else{//边缘像素采用近邻插值 for (k = 0; k<pixelByte; k++) *(transBuf + h * lineByteOut + w*pixelByte + k) = *(dataBuf + Iv*lineByte + Iu*pixelByte + k); } } } } void Bitmap::rotate(float angle) { int pixelByte = bitCount / 8;//每像素字节数 int lineByte = (width_p*bitCount / 8 + 3) / 4 * 4;//原图像每行像素的字节数 // 旋转角度(弧度), 将旋转角度从度转换到弧度 float fRotateAngle = 2 * 3.1415926*angle / 360; // 输入图像四个角的坐标,以图像中心为坐标系原点 float fSrcX1, fSrcY1, fSrcX2, fSrcY2, fSrcX3, fSrcY3, fSrcX4, fSrcY4; // 旋转后四个角的坐标,以图像中心为坐标系原点 float fDstX1, fDstY1, fDstX2, fDstY2, fDstX3, fDstY3, fDstX4, fDstY4; // 计算旋转角度的正弦 float fSina = (float)sin((double)fRotateAngle); // 计算旋转角度的余弦 float fCosa = (float)cos((double)fRotateAngle); // 计算原图的四个角的坐标,以图像中心为坐标系原点 fSrcX1 = (float)(-(width_p - 1) / 2); fSrcY1 = (float)((height_p - 1) / 2); fSrcX2 = (float)((width_p - 1) / 2); fSrcY2 = (float)((height_p - 1) / 2); fSrcX3 = (float)(-(width_p - 1) / 2); fSrcY3 = (float)(-(height_p - 1) / 2); fSrcX4 = (float)((width_p - 1) / 2); fSrcY4 = (float)(-(height_p - 1) / 2); // 计算新图四个角的坐标,以图像中心为坐标系原点 fDstX1 = fCosa * fSrcX1 + fSina * fSrcY1; fDstY1 = -fSina * fSrcX1 + fCosa * fSrcY1; fDstX2 = fCosa * fSrcX2 + fSina * fSrcY2; fDstY2 = -fSina * fSrcX2 + fCosa * fSrcY2; fDstX3 = fCosa * fSrcX3 + fSina * fSrcY3; fDstY3 = -fSina * fSrcX3 + fCosa * fSrcY3; fDstX4 = fCosa * fSrcX4 + fSina * fSrcY4; fDstY4 = -fSina * fSrcX4 + fCosa * fSrcY4; // 旋转后输出图像宽度 int width_pOut = (int)(max(fabs(fDstX4 - fDstX1), fabs(fDstX3 - fDstX2)) + 0.5); // 旋转后输出图像高度 int height_pOut = (int)(max(fabs(fDstY4 - fDstY1), fabs(fDstY3 - fDstY2)) + 0.5); // 旋转后输出图像每行的字节数 int lineByteOut = (width_pOut*pixelByte + 3) / 4 * 4; //分配缓冲区,存放旋转结果 unsigned char* transBuf = new unsigned char[lineByteOut*height_pOut]; memset(transBuf, 0, lineByteOut*height_pOut); // 两个常数,这样不用以后每次都计算了 float f1 = (float)(-0.5 * (width_pOut - 1) * fCosa + 0.5 * (height_pOut - 1) * fSina + 0.5 * (width_p - 1)); float f2 = (float)(-0.5 * (width_pOut - 1) * fSina - 0.5 * (height_pOut - 1) * fCosa + 0.5 * (height_p - 1)); // 循环变量,输出图像坐标 int i, j; //循环变量,像素的每个通道 int k; //输出图像在输入图像中待插值的位置坐标,必须浮点型 int coordinateX, coordinateY; // 最近邻插值旋转 for (i = 0; i < height_pOut; i++) { for (j = 0; j < width_pOut; j++) { // 输出图像像素(j,i)映射到输入图像的坐标,近邻插值取整数 coordinateX = (int)(j * fCosa - i * fSina + f1 + 0.5); coordinateY = (int)(j * fSina + i * fCosa + f2 + 0.5); // 判断是否在输入图像范围内 if ((coordinateX >= 0) && (coordinateX <= width_p) && (coordinateY >= 0) && (coordinateY <=height_p)) { //将图像每个通道的数据进行分别插值,彩色图像pixelByte为3, //灰度图像pixelByte为1 for (k = 0; k<pixelByte; k++) *(transBuf + i*lineByteOut + j*pixelByte + k) = *(dataBuf + coordinateY*lineByte + coordinateX*pixelByte + k); } //else //{ // // 对于不在原图中的像素,赋值为255 // for (k = 0; k<pixelByte; k++) // *(transBuf + i*lineByteOut + j*pixelByte + k) = 255; //} } } delete[] dataBuf; dataBuf = transBuf; }
测试
#include"bmp.h" #include<iostream> using namespace std; int main() { char* fileName = "qianxun.bmp"; Bitmap* bmp = new Bitmap(); bmp->read(fileName); bmp->translation(10, 10); //bmp->zoom(2, 2, 'n');//'n','l','c';默认为'n'邻近插值缩放 //bmp->rotate(90); bmp->write("translation.bmp"); delete bmp; return 1; }