在图像的像素点灰度值基本集中在一个段落的时候,图像中物体与物体之间的区别是很不明显的(也就是说图像的对比度比较低)。这种情况可以采用“线性变换”来对图像加以处理。
图像的线性变换,是比较容易理解的。由线性变换公式y=kx+b可以想到,假设图像的像素点灰度值为x,经由线性变换输出为y,这个时候用y替换掉原像素点的灰度值x则实现了线性映射。其作用是当图像像素的灰度值集中在一定范围如[a,b]时,可以经由线性变换公式将其映射到新的范围[c,d]。如果将c取为0,将d取为255,则映射到整个灰度级中,即最大可能的加大了图像的对比度。经过上面的分析,再由数学解方程的思路我们知道只要定出a,b,c,d四个值则可以得到线性变换公式,进而由线性变换公式来对图像进行线性变换。
由上面的分析我们可以大致的知道线性变换的应用是要在一些灰度值相对集中的图像才能取得好的效果的。因为在线性变换之前,我们先搜索出图像的灰度最小、最大值作为a,b,如果a接近0而b接近255则效果是可想而知的。是故在应用线性变换的过程中需要特别注意的是如何排除个别点的干扰。
ok,废话不多说,主要实现代码如下:
/*******************图像处理部分******************/ /*******************图像的线性变换******************/ /*灰度化*/ for (int hnum = 0; hnum < MYDRAW_HEIGHT; hnum++) for (int wnum = 0; wnum < MYDRAW_WIDTH; wnum++) { int pixel_point = hnum*write_width + wnum * 3;//指向图像像素点 pColorData[pixel_point] = pColorData[pixel_point + 1] = pColorData[pixel_point + 2] = (pColorData[pixel_point] + pColorData[pixel_point + 1] + pColorData[pixel_point + 2]) / 3; } /*灰度化*/ /*计算图像像素点的最大最小灰度值*/ BYTE min = 255;//最小值 BYTE max = 0;//最大值 for (int hnum = 0; hnum < MYDRAW_HEIGHT; hnum++) for (int wnum = 0; wnum < MYDRAW_WIDTH; wnum++) { int pixel_point = hnum*write_width + wnum * 3;//指向图像像素点 if (pColorData[pixel_point]>max) { max = pColorData[pixel_point]; } if (pColorData[pixel_point]<min) { min = pColorData[pixel_point]; } } printf("最大灰度值:%d,最小灰度值:%d\n", max, min); /*计算图像像素点的最大最小灰度值*/ /*线性变换*/ double k = (255 - 0) / (double)(max - min); double b = -k*min; for (int hnum = 0; hnum < MYDRAW_HEIGHT; hnum++) for (int wnum = 0; wnum < MYDRAW_WIDTH; wnum++) { int pixel_point = hnum*write_width + wnum * 3;//指向图像像素点 pColorDataMid[pixel_point] = pColorDataMid[pixel_point + 1] = pColorDataMid[pixel_point + 2] = k*pColorData[pixel_point] + b; } /*线性变换*/ /*******************图像的线性变换******************/ /*******************图像处理部分******************/处理结果如下:
原图像
线性变换后图像
整个工程代码如下:
#include <string.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include<time.h>//时间相关头文件,可用其中函数计算图像处理速度 #define WIDTHBYTES(bits) (((bits)+31)/32*4)//用于使图像宽度所占字节数为4byte的倍数 #define MYDRAW_WIDTH 375 //目标图像宽度 #define MYDRAW_HEIGHT 300 //目标图像高度 typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; typedef long LONG; //位图文件头信息结构定义 //其中不包含文件类型信息(由于结构体的内存结构决定,要是加了的话将不能正确读取文件信息) typedef struct tagBITMAPFILEHEADER { DWORD bfSize; //文件大小 WORD bfReserved1; //保留字,不考虑 WORD bfReserved2; //保留字,同上 DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和 } BITMAPFILEHEADER; //信息头BITMAPINFOHEADER,也是一个结构,其定义如下: typedef struct tagBITMAPINFOHEADER{ //public: DWORD biSize; //指定此结构体的长度,为40 LONG biWidth; //位图宽 LONG biHeight; //位图高 WORD biPlanes; //平面数,为1 WORD biBitCount; //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32 DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩 DWORD biSizeImage; //实际位图数据占用的字节数 LONG biXPelsPerMeter; //X方向分辨率 LONG biYPelsPerMeter; //Y方向分辨率 DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数) DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的 } BITMAPINFOHEADER; void main() { long now = 0; now = clock();//存储图像处理开始时间 BITMAPFILEHEADER bitHead, writebitHead; BITMAPINFOHEADER bitInfoHead, writebitInfoHead; FILE* pfile;//输入文件 FILE* wfile;//输出文件 char strFile[50] = "线性变换.bmp";//打开图像路径,BMP图像必须为24位真彩色格式 char strFilesave[50] = "变换结果.bmp";//处理后图像存储路径 fopen_s(&pfile, strFile, "rb");//文件打开图像 fopen_s(&wfile, strFilesave, "wb");//打开文件为存储修改后图像做准备 //读取位图文件头信息 WORD fileType; fread(&fileType, 1, sizeof(WORD), pfile); fwrite(&fileType, 1, sizeof(WORD), wfile); if (fileType != 0x4d42) { printf("file is not .bmp file!"); return; } //读取位图文件头信息 fread(&bitHead, 1, sizeof(tagBITMAPFILEHEADER), pfile); writebitHead = bitHead;//由于截取图像头和源文件头相似,所以先将源文件头数据赋予截取文件头 //读取位图信息头信息 fread(&bitInfoHead, 1, sizeof(BITMAPINFOHEADER), pfile); writebitInfoHead = bitInfoHead;//同位图文件头相似 writebitInfoHead.biHeight = MYDRAW_HEIGHT;//为截取文件重写位图高度 writebitInfoHead.biWidth = MYDRAW_WIDTH;//为截取文件重写位图宽度 int mywritewidth = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);//BMP图像实际位图数据区的宽度为4byte的倍数,在此计算实际数据区宽度 writebitInfoHead.biSizeImage = mywritewidth*writebitInfoHead.biHeight;//计算位图实际数据区大小 writebitHead.bfSize = 54 + writebitInfoHead.biSizeImage;//位图文件头大小为位图数据区大小加上54byte fwrite(&writebitHead, 1, sizeof(tagBITMAPFILEHEADER), wfile);//写回位图文件头信息到输出文件 fwrite(&writebitInfoHead, 1, sizeof(BITMAPINFOHEADER), wfile);//写回位图信息头信息到输出文件 int width = bitInfoHead.biWidth; int height = bitInfoHead.biHeight; //分配内存空间把源图存入内存 int l_width = WIDTHBYTES(width*bitInfoHead.biBitCount);//计算位图的实际宽度并确保它为4byte的倍数 int write_width = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);//计算写位图的实际宽度并确保它为4byte的倍数 BYTE *pColorData = (BYTE *)malloc(height*l_width);//开辟内存空间存储图像数据 memset(pColorData, 0, height*l_width); BYTE *pColorDataMid = (BYTE *)malloc(mywritewidth*MYDRAW_HEIGHT);//开辟内存空间存储图像处理之后数据 memset(pColorDataMid, 0, mywritewidth*MYDRAW_HEIGHT); long nData = height*l_width; long write_nData = mywritewidth*MYDRAW_HEIGHT;//截取的位图数据区长度定义 //把位图数据信息读到数组里 fread(pColorData, 1, nData, pfile);//图像处理可通过操作这部分数据加以实现 /*******************图像处理部分******************/ /*******************图像的线性变换******************/ /*灰度化*/ for (int hnum = 0; hnum < MYDRAW_HEIGHT; hnum++) for (int wnum = 0; wnum < MYDRAW_WIDTH; wnum++) { int pixel_point = hnum*write_width + wnum * 3;//指向图像像素点 pColorData[pixel_point] = pColorData[pixel_point + 1] = pColorData[pixel_point + 2] = (pColorData[pixel_point] + pColorData[pixel_point + 1] + pColorData[pixel_point + 2]) / 3; } /*灰度化*/ /*计算图像像素点的最大最小灰度值*/ BYTE min = 255;//最小值 BYTE max = 0;//最大值 for (int hnum = 0; hnum < MYDRAW_HEIGHT; hnum++) for (int wnum = 0; wnum < MYDRAW_WIDTH; wnum++) { int pixel_point = hnum*write_width + wnum * 3;//指向图像像素点 if (pColorData[pixel_point]>max) { max = pColorData[pixel_point]; } if (pColorData[pixel_point]<min) { min = pColorData[pixel_point]; } } printf("最大灰度值:%d,最小灰度值:%d\n", max, min); /*计算图像像素点的最大最小灰度值*/ /*线性变换*/ double k = (255 - 0) / (double)(max - min); double b = -k*min; for (int hnum = 0; hnum < MYDRAW_HEIGHT; hnum++) for (int wnum = 0; wnum < MYDRAW_WIDTH; wnum++) { int pixel_point = hnum*write_width + wnum * 3;//指向图像像素点 pColorDataMid[pixel_point] = pColorDataMid[pixel_point + 1] = pColorDataMid[pixel_point + 2] = k*pColorData[pixel_point] + b; } /*线性变换*/ /*******************图像的线性变换******************/ /*******************图像处理部分******************/ fwrite(pColorDataMid, 1, write_nData, wfile); //将处理完图像数据区写回文件 fclose(pfile); fclose(wfile); printf("图像处理完成\n"); printf("运行时间为:%dms\n", int(((double)(clock() - now)) / CLOCKS_PER_SEC * 1000));//输出图像处理花费时间信息 }