关闭

几种常见的图像处理的方法

标签: 图像处理byteheaderdeleteimfile
6871人阅读 评论(0) 收藏 举报
分类:

本章所涉及的方法有:灰度化彩色图像,将图像转换为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

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:104964次
    • 积分:1499
    • 等级:
    • 排名:千里之外
    • 原创:37篇
    • 转载:9篇
    • 译文:0篇
    • 评论:14条
    文章分类
    最新评论