转自:http://blog.csdn.net/smells2/article/details/7378840
=============================================
本章所涉及的方法有:灰度化彩色图像,将图像转换为ASNII码文件,直方图均衡化,伽马校正,哈尔小波变换。
0.知识储备
这里我们处理的是bmp格式的图像,bmp格式的文件有3个文件头,第一个文件头大小为14个字节,主要是对整个文件的信息的存储。
- typedef struct tagBITMAPFILEHEADER {
- WORD bfType;
- DWORD bfSize;
- WORD bfReserved1;
- WORD bfReserved2;
- DWORD bfOffBits;
- } BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
第二个文件头大小为50个字节,主要存储了图像的信息,比如,图像有多少行,多少列,大小等等。
- typedef struct tagBITMAPINFOHEADER{
- DWORD biSize;
- LONG biWidth;
- LONG biHeight;
- WORD biPlanes;
- WORD biBitCount;
- DWORD biCompression;
- DWORD biSizeImage;
- LONG biXPelsPerMeter;
- LONG biYPelsPerMeter;
- DWORD biClrUsed;
- DWORD biClrImportant;
- } BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
第三个文件头,是图像的调色板,这里我们使用的文件是24位真彩,所以没有调色板,这个文件头不存在,所以我就不列在下面了。
注意:bmp格式图像数据是倒着存贮的,具体可以参考百度百科http://baike.baidu.com/view/7671.htm#2。
1.将彩色图像转换为灰度图像
我们都知道,图像是一像素为单位的,一个像素有三个色彩分量组成,即RGB(红绿蓝),每一个分量由8位(一个字节)组成,范围是0-255 。灰度图像可由彩色图像转化,可以使用公式gray = 0.3*red+0.59*green+0.11*blue,并且三个分量的值相等,即red = gray,green=gray,blue=gray。那么,我们的图像就变成了灰度图像。
- /********************************************
- * 将文件转换成灰度图像 *
- ********************************************/
- int CBitMapFile::turn2Gray()
- {
- if (m_imageArray == NULL)
- {
- return -2;
- }
- for (int i =0,j= 0,k=0;i<m_sizeImage;)
- {
- //转换为灰度图像,注意填充的字节
- BYTE blue,green,red,gray;
- blue = m_imageArray[i];
- green = m_imageArray[i+1];
- red = m_imageArray[i+2];
- m_realColorImageArray[k] = blue;
- m_realColorImageArray[k+1] = green;
- m_realColorImageArray[k+2] = red;
- gray = (BYTE)(0.3*red+0.59*green+0.11*blue);
- m_imageArray[i] = gray;
- m_imageArray[i+1] = gray;
- m_imageArray[i+2] = gray;
- m_realImageArray[j] = m_imageArray[i];
- i += 3;
- k += 3;
- j++;
- 跳过填充字节
- if (j % m_width == 0)
- {
- i += m_addByte;
- }
- }
- return 1;
- }
2.图像转换为ASNII码文件
三个步骤:(1)提取图像数据(2)建立映射表(3)处理灰度并映射
映射表我建立的是8个字符'@','$','#','%','!','~','^','`'。
把灰度值除上32将其范围映射到0-7这个范围内。
将灰度值按照映射表映射。
- /******************************************
- *对图像的每个像素做文本映射,最终写入文件*
- ******************************************/
- int CBitMapFile::turn2Txt(CFile& txtFile)
- {
- char* _txtBuf = new char[m_width*m_height+2*m_height];
- memset(_txtBuf,0,m_width*m_height+2*m_height);
- //文本映射
- char txtMap[8] = {'@','$','#','%','!','~','^','`'};
- char* _buf = new char[m_width+2];
- memset(_buf,0,m_width+2);
- //TRACE(_T("\'\\r\'=%x,\'\\n\'=%x"),'\r','\n');
- for (int i = m_height-1;i>=0;i--)
- {
- for (int j = 0;j<m_width;j++)
- {
- _buf[j] = txtMap[m_realImageArray[i*m_width+j]>>5];
- }
- _buf[m_width] = '\r';
- _buf[m_width+1] = '\n';
- for (int k=0;k<m_width+2;k++)
- {
- _txtBuf[(m_height-1-i)*m_width+k+(m_height-1-i)*2] = _buf[k];
- }
- }
- txtFile.Write(_txtBuf,sizeof(char)*(m_width*m_height+2*m_height));
- delete _txtBuf;
- delete _buf;
- return 1;
- }
3.直方图均衡化
每一幅图像都有自己的直方图
图像取自冈萨雷斯的《数字图像处理》第三版
这幅图像的灰度大部分集中在较高的灰度,均衡化就使图像在概率率较大的地方稀疏一点,概率较小的地方稠密一点。
方法是:(1)统计各个灰度的概率(2)归一化(3)画直方图(4)累计概率(5)取整得到映射(6)将原灰度值映射到新灰度值
- /********************************************
- * 对bmp文件的每个像素的灰度值进行统计 *
- ********************************************/
- int CBitMapFile::addupLevel()
- {
- for (int i = 0;i<m_width*m_height;i++)
- {
- m_grayStatistic[m_realImageArray[i]] += 1;
- }
- for (int i = 0;i<256;i++)
- {
- if (m_grayStatistic[i]>m_max)
- {
- m_max = m_grayStatistic[i];
- }
- }
- //TRACE(_T("m_max = %d\n"),m_max);
- return 1;
- }
- /********************************************
- * 对bmp文件的每个像素的灰度值进行归一化 *
- * 并计算每个像素灰度值的概率 *
- ********************************************/
- int CBitMapFile::turn2One()
- {
- for (int i =0;i<256;i++)
- {
- m_grayTurn2OneData[i] = (double)m_grayStatistic[i]/m_max;
- m_grayFrequencyPerLevel[i] = (double)m_grayStatistic[i]/(m_width*m_height);
- }
- m_maxFrequency = m_max/(m_width*m_height);
- return 1;
- }
- /********************************************
- * 清除统计数组、归一化数组、频率数组 *
- ********************************************/
- void CBitMapFile::clearup()
- {
- memset(m_grayStatistic,0,sizeof(LONG)*256);
- memset(m_grayTurn2OneData,0,sizeof(double)*256);
- memset(m_grayFrequencyPerLevel,0,sizeof(double)*256);
- m_max = 0;
- m_maxFrequency = 0.0f;
- }
- /********************************************
- * 灰度均衡化 *
- ********************************************/
- void CBitMapFile::equation(CFile& file)
- {
- double _temp =0.0f;
- double* _array = new double[256];
- memset(_array,0,sizeof(double)*256);
- int* _intArray = new int[256];
- memset(_intArray,0,sizeof(int)*256);
- BYTE* _writeArray = new BYTE[m_sizeImage];
- memset(_writeArray,0,sizeof(BYTE)*m_sizeImage);
- for(int i = 0;i<256;i++)
- {
- _array[i] = ((m_grayFrequencyPerLevel[i])*255)+_temp;
- _temp = _array[i];
- }
- for (int i = 0;i<256;i++)
- {
- _intArray[i] = (int)(_array[i]);
- }
- for (int i = 0,j = 0;i<m_sizeImage;)
- {
- _writeArray[i] = _intArray[m_realImageArray[j]];
- _writeArray[i+1] = _intArray[m_realImageArray[j]];
- _writeArray[i+2] = _intArray[m_realImageArray[j]];
- j++;
- i += 3;
- if (j%m_width == 0)
- {
- for (int k = 0;k<m_addByte;k++)
- {
- _writeArray[i+k] = 0;
- }
- i += m_addByte;
- }
- }
- file.Write(&m_file_header,sizeof(BITMAPFILEHEADER));
- file.Write(&m_info_header,sizeof(BITMAPINFOHEADER));
- file.Write(_writeArray,sizeof(BYTE)*m_sizeImage);
- delete _array;
- delete _intArray;
- delete _writeArray;
- }
4.伽马校正
伽马校正其实很简单,就是将旧的灰度值通过一个由灰度值为底,伽马次幂的映射得到新的灰度值,它的做用是,对于不同的伽马系数的选取,会使得某些较亮的图片,变得对比度适当。
- /********************************************
- * 伽马校正 *
- ********************************************/
- void CBitMapFile::gammaCorrection(double gamma,CFile& file)
- {
- //数据准备
- double* _correctData = new double[m_width*m_height];
- BYTE* _writeArray = new BYTE[m_sizeImage];
- memset(_writeArray,0,sizeof(BYTE)*m_sizeImage);
- memset(_correctData,0,sizeof(double)*m_width*m_height);
- //算法
- for (int i = 0;i<m_width*m_height;i++)
- {
- _correctData[i] = (double)m_realImageArray[i]/255;
- _correctData[i] = pow(_correctData[i],gamma)*255;
- }
- //写入文件准备
- for (int i = 0,j = 0;i<m_sizeImage;)
- {
- _writeArray[i] = (BYTE)_correctData[j];
- _writeArray[i+1] = (BYTE)_correctData[j];
- _writeArray[i+2] = (BYTE)_correctData[j];
- j++;
- i += 3;
- if (j%m_width == 0)
- {
- for (int k = 0;k<m_addByte;k++)
- {
- _writeArray[i+k] = 0;
- }
- i += m_addByte;
- }
- }
- //写入文件
- file.Write(&m_file_header,sizeof(BITMAPFILEHEADER));
- file.Write(&m_info_header,sizeof(BITMAPINFOHEADER));
- file.Write(_writeArray,sizeof(BYTE)*m_sizeImage);
- delete _writeArray;
- delete _correctData;
- }
这个是伽马选择7.5的效果
4.哈尔小波变换(HWT,haar wavelet tansform)
这个是小波变换里最简单的一个,由于涉及较多的数学,这里就不做赘述了,只是附上程序。(程序使用的矩阵乘法是我自己写的,效率较低,经过测试在128*128的大小的图像效果可以,再大就会出现未响应的现象。)
注意,哈尔小波变换的矩阵是偶数*偶数的,所以图片的行列也必须是偶数。
Matrix.h
- #pragma once
- #define ZERO 0 //零阵
- #define IDENTITY 1//单位阵
- #define HAAR 2//HAAR小波矩阵
- #define UNSQUARE -1//非方阵
- #define UNSAME -2//非同型矩阵
- #define UNSAME -2//非同型矩阵
- #define UNEVEN -3//行列非偶数
- class CMatrix
- {
- public:
- CMatrix(int r,int c);
- CMatrix(CMatrix& m);
- CMatrix(double* d,int r,int c);
- CMatrix(BYTE* d,int r,int c);
- ~CMatrix();
- int inital(int type);//初始化为单位阵,或特殊矩阵(以后扩展)
- #ifdef _DEBUG
- void display();
- #endif
- //
- public:
- CMatrix& transpose();//矩阵的转置
- void nonZero();
- //
- public:
- CMatrix& operator=(CMatrix& m1);
- friend CMatrix operator+(CMatrix& m1,CMatrix& m2);
- friend CMatrix operator-(CMatrix& m1,CMatrix& m2);
- friend CMatrix operator*(CMatrix& m1,double& a);
- friend CMatrix operator*(CMatrix& m1,CMatrix& m2);
- //
- public:
- inline int getRow(){return m_row;}
- inline int getCol(){return m_col;}
- //inline CString getName(){return m_name;}
- inline double getElem(int r,int c){return m_data[r*m_row+c];}
- inline double* getData(){return m_data;}
- //
- protected:
- int m_row;
- int m_col;
- double* m_data;
- // CString m_name;
- };
Matrix.cpp
- #include "StdAfx.h"
- #include "MatrixBase.h"
- #ifdef _DEBUG
- #include <iostream>
- #endif
- CMatrix::CMatrix(int r,int c)
- {
- m_row = r;
- m_col = c;
- m_data = new double[r*c];
- memset(m_data,0,sizeof(double)*r*c);
- }
- CMatrix::CMatrix(double* d,int r,int c)
- {
- m_row = r;
- m_col = c;
- m_data = new double[r*c];
- for (int i = 0;i<r*c;i++)
- {
- m_data[i] = d[i];
- }
- }
- CMatrix::CMatrix(BYTE* d,int r,int c)
- {
- m_row = r;
- m_col = c;
- m_data = new double[r*c];
- for (int i = 0;i<r*c;i++)
- {
- m_data[i] = (double)d[i];
- }
- }
- CMatrix::CMatrix(CMatrix& m)
- {
- this->m_row = m.m_row;
- this->m_col = m.m_col;
- m_data = new double[m_row*m_col];
- for (int i = 0;i<m_row*m_col;i++)
- {
- this->m_data[i] = m.m_data[i];
- TRACE(_T("data[%d]=%f\n"),i,this->m_data[i]);
- }
- }
- CMatrix::~CMatrix()
- {
- delete[] m_data;
- }
- int CMatrix::inital(int type)
- {
- switch(type)
- {
- case IDENTITY:
- if (m_row != m_col)
- {
- return UNSQUARE;
- }
- for (int i=0;i<m_col;i++)
- {
- m_data[i*m_row+i] = 1;
- }
- break;
- case HAAR:
- if (m_row != m_col)
- {
- return UNSQUARE;
- }
- if (m_row%2 != 0)
- {
- return UNEVEN;
- }
- for(int i = 0;i<m_row/2;i++)
- {
- m_data[i*m_row+i*2] = 1;
- m_data[i*m_row+i*2+1] = 1;
- }
- for (int i = m_row/2,j = 0;i<m_row;i++,j++)
- {
- m_data[i*m_row+j*2] = -1;
- m_data[i*m_row+j*2+1] = 1;
- }
- break;
- default:
- break;
- }
- return type;
- }
- CMatrix& CMatrix::operator=(CMatrix& m1)
- {
- ASSERT(m_row == m1.getRow() && m_col == m1.getCol());
- for (int i = 0;i<m_row*m_col;i++)
- {
- m_data[i] = m1.getElem(i/m_row,i%m_row);
- TRACE(_T("\'=\'data[%d]=%f\n"),i,m_data[i]);
- }
- return *this;
- }
- CMatrix operator+(CMatrix& m1,CMatrix& m2)
- {
- ASSERT(m1.m_row == m2.m_row && m1.m_col==m2.m_col);
- CMatrix ret(m1.m_row,m1.m_col);
- for (int i = 0;i<m1.m_row*m1.m_col;i++)
- {
- ret.m_data[i] = m1.m_data[i]+m2.m_data[i];
- TRACE(_T("ret[%d]=%f\n"),i,ret.m_data[i]);
- }
- return ret;
- }
- CMatrix operator-(CMatrix& m1,CMatrix& m2)
- {
- ASSERT(m1.m_row == m2.m_row && m1.m_col==m2.m_col);
- CMatrix ret(m1.m_row,m1.m_col);
- for (int i = 0;i<m1.m_row*m1.m_col;i++)
- {
- ret.m_data[i] = m1.m_data[i]-m2.m_data[i];
- TRACE(_T("ret[%d]=%f\n"),i,ret.m_data[i]);
- }
- return ret;
- }
- CMatrix operator*(CMatrix& m1,double& a)
- {
- CMatrix ret(m1);
- for (int i = 0;i<m1.m_row*m1.m_col;i++)
- {
- ret.m_data[i] *=a;
- TRACE(_T("ret[%d]=%f\n"),i,ret.m_data[i]);
- }
- return ret;
- }
- CMatrix operator*(CMatrix& m1,CMatrix& m2)
- {
- ASSERT(m1.m_col == m2.m_row);
- CMatrix ret(m1.m_row,m2.m_col);
- for (int i= 0;i<m1.m_row;i++)
- {
- for (int j = 0;j<m2.m_col;j++)
- {
- double _temp = 0;
- for (int k = 0;k<m1.m_col;k++)
- {
- _temp += m1.m_data[i*m1.m_row+k]*m2.m_data[k*m2.m_row+j];
- }
- ret.m_data[i*m1.m_row+j] = _temp;
- }
- }
- return ret;
- }
- CMatrix& CMatrix::transpose()
- {
- double* _newData = new double[m_row*m_col];
- for (int i = 0;i<m_row;i++)
- {
- for (int j=0;j<m_col;j++)
- {
- _newData[j*m_col+i] = m_data[i*m_row+j];
- }
- }
- delete m_data;
- m_data = _newData;
- return *this;
- }
- void CMatrix::nonZero()
- {
- for (int i = 0;i<m_row;i++)
- {
- for (int j = 0;j<m_col;j++)
- {
- if(m_data[i*m_row+j]<0)
- {
- m_data[i*m_row+j]=0;
- }
- }
- }
- }
- #ifdef _DEBUG
- void CMatrix::display()
- {
- std::cout<<"[\n";
- for (int i = 0;i<m_row*m_col;i++)
- {
- std::cout<<m_data[i]<<" ,";
- if ((i+1)%m_row == 0)
- {
- std::cout<<std::endl;
- }
- }
- std::cout<<"]"<<std::endl;
- }
- #endif
哈尔小波变换函数
- /********************************************
- * HAAR小波变换 *
- ********************************************/
- void CBitMapFile::haarTransform(CFile& file)
- {
- CMatrix imageMatrix(m_realImageArray,m_height,m_width);
- CMatrix haarMatrix_r(m_height,m_height);
- CMatrix haarMatrix_c(m_width,m_width);
- #ifdef _DEBUG
- imageMatrix.display();
- #endif
- haarMatrix_c.inital(HAAR);
- haarMatrix_c = haarMatrix_c.transpose();
- haarMatrix_r.inital(HAAR);
- imageMatrix = haarMatrix_r*imageMatrix;
- double _d = 0.25*sqrt(2.0);
- imageMatrix = imageMatrix*_d;
- imageMatrix = imageMatrix*haarMatrix_c;
- imageMatrix = imageMatrix*_d;
- imageMatrix.nonZero();
- #ifdef _DEBUG
- imageMatrix.display();
- #endif
- double* _correctData = imageMatrix.getData();
- BYTE* _writeArray = new BYTE[m_sizeImage];
- memset(_writeArray,0,sizeof(BYTE)*m_sizeImage);
- for (int i = 0,j = 0;i<m_sizeImage;)
- {
- _writeArray[i] = (BYTE)_correctData[j];
- _writeArray[i+1] = (BYTE)_correctData[j];
- _writeArray[i+2] = (BYTE)_correctData[j];
- j++;
- i += 3;
- if (j%m_width == 0)
- {
- for (int k = 0;k<m_addByte;k++)
- {
- _writeArray[i+k] = 0;
- }
- i += m_addByte;
- }
- }
- file.Write(&m_file_header,sizeof(BITMAPFILEHEADER));
- file.Write(&m_info_header,sizeof(BITMAPINFOHEADER));
- file.Write(_writeArray,sizeof(BYTE)*m_sizeImage);
- delete _writeArray;
- //delete _correctData;
- }
处理后图像
由于bmp格式是倒着存储图像数据的,因为个人时间原因没有将其倒回,所以出现这种现象。
实验软件可以在这里下载:http://download.csdn.net/detail/smells2/4162515