数字图像处理基本算法实现(2)--section3.3直方图处理

      从上一个section3.2到现在又过去一段时间了,断断续续终于把section3.3的直方图那些事弄出来了,还是那样:不求速度与健壮性,但求自己取实现一下,尽量自己去写代码,不用现成的函数。

     《数字图像处理 Digital Image Processing-3E冈萨雷斯》(后面简称《DIP》)的section3.3是直方图处理有直方图均衡,直方图规则化,局部直方图处理以及用局部直直方图做图像增强,主要是这几个,所以这次就把这几个算法实现了一下,附带着写了一个简单的显示直方图的函数,能够以曲线或者条状显示直方图,1个通道或者3个通道。当然使用了一些OpenCV的函数。

       算法内容都是按照书上来的,所以废话不说,先上声明:

enum HistType{Line_hist, Bin_hist};
class section_3_3
{
public:
	//calculate image histogram;
	bool getImgHist(Mat &src, vector< vector<int> > &hist, int chanels_use);
	//equalize given image
	Mat & equalizeImgHist(Mat &src);
	//show given histogram--note! this change the histogram! 
	bool showHist(string wndname, vector< vector<int> > &hist, HistType histType, int thickness=1,bool bSave=false, string savename=string());
	//normalize histogram to given range; 
	void normalizeHist(vector< vector<int> > &hist, int maxLeval);
	//hist matching or hist normalization;
	//use the point given histogram curve;
	Mat& histMatching(Mat &src, vector<cv::Point> &hist_graph);
	//use the given histogram;
	Mat& histMatching(Mat &src, vector<int> &dest_hist);
	//equalize use local hist;
	Mat& localEqualize(Mat &src, int rgSize=3);
	//image enhancement use local hist;
	Mat& enhanceImg(Mat &src, int rgSize=3, float E=4.0f, float k0=0.4f, float k1=0.02f, float k2=0.4f);

private:
	Mat m_destMat;
	Mat m_showMat;
};


 

       主要的就是前面说的那几个函数,采用了STL的vector来存放直方图,其中需要注意的是normalizeHist函数,其实就是将直方图的bin的最大值限制到maxLeval,方便直方图的显示,histMatching是直方图规则化或者叫直方图匹配,其中给出了两种实现,一个用vector点集指定直方图曲线的几个拐点,另一个是直接输入一个目标直方图,将源图像的直方图变换为与目标直方图一样(大体一样)。下面是函数实现--有点长吐舌头

bool section_3_3::getImgHist(Mat &src, vector< vector<int> > &hist, int chanels_use)
{
	if(src.channels() < chanels_use || !(chanels_use==1||chanels_use==3))
		return false;

	Mat temp_gray;
	if(chanels_use ==1 && src.channels()==3)
		cvtColor(src, temp_gray, COLOR_BGR2GRAY);

	//create hist vectors;
	vector<int> hh(256, 0);
	for(int i=0; i<chanels_use; ++i)
	{
		hist.push_back(hh);
	}
	//calculate pixels
	for (int i=0; i<src.rows; ++i)
	{
		uchar *src_ptr = src.ptr<uchar>(i);
		for (int j=0; j<src.cols; ++j)
		{
			if(chanels_use==1)
			{
				++hist[0][src_ptr[j]];
			}
			else
			{
				int j3 = j*3;
				++hist[0][src_ptr[j3]];
				++hist[1][src_ptr[j3+1]];
				++hist[2][src_ptr[j3+2]];
			}
		}
	}

	return true;
}

//normalize hist to the given range, maxLeval gives the upper bound, lower bound is 0;
void section_3_3::normalizeHist(vector< vector<int> > &hist, int maxLeval)
{
	for (size_t i=0; i<hist.size(); ++i)
	{	
		int maxvalue = 0;
		for (size_t j=0; j<hist[i].size(); ++j)
		{
			if(hist[i][j]>maxvalue)
				maxvalue = hist[i][j];
		}
		//if the maxvalue is less than maxLeval, no need to change;
		if(maxvalue <= maxLeval)
			continue;
		//normalize the hist---note!-this change the given histogram;
		for (size_t j=0; j<hist[i].size(); ++j)
		{
			hist[i][j] = (int)(hist[i][j]*maxLeval/(float)(maxvalue));
		}
	}
}
//show histogram in image, can choose hist type:bin or line, save it or not;
bool section_3_3::showHist(string wndname, vector< vector<int> > &hist, HistType histType, int thickness, bool bSave, string savename)
{
	int width = 512+20;
	int heightB = 400; //big
	int heightS = 200;	//small
	int heightS3 = 600;	//3*small
	if(thickness > 2)
		thickness = 2;

	//curve hist ;
	if(histType == Line_hist)
	{
		m_showMat.create(heightB+20, width, CV_8UC3);
		normalizeHist(hist, heightB-10);
		if(thickness==-1)
			thickness = 1;
	}
	else //bin hist;
	{	
		if(hist.size() == 3)
		{
			m_showMat.create(heightS3+20, width, CV_8UC3);
			normalizeHist(hist, heightS-10);
		}
		else
		{
			m_showMat.create(heightB+20, width, CV_8UC3);
			normalizeHist(hist, heightB-10);
		}
	}

	//black background
	m_showMat = Scalar::all(0);
	
	//if bin hist and 3 channels, up to down=channel 0 to 2;
	//if line hist all in one;
	int chanel = hist.size();
	for(int i=1; i<256;)
	{
		if (histType == Line_hist)
		{
			if(chanel == 1)
			{
				line(m_showMat, cv::Point(10+2*(i-1), heightB-hist[0][i-1]), cv::Point(10+2*i, heightB-hist[0][i]), Scalar::all(255), thickness);
			}
			else
			{
				line(m_showMat, cv::Point(10+2*(i-1), heightB-hist[0][i-1]), cv::Point(10+2*i, heightB-hist[0][i]), Scalar(255,0,0), thickness);
				line(m_showMat, cv::Point(10+2*(i-1), heightB-hist[1][i-1]), cv::Point(10+2*i, heightB-hist[1][i]), Scalar(0,255,0), thickness);
				line(m_showMat, cv::Point(10+2*(i-1), heightB-hist[2][i-1]), cv::Point(10+2*i, heightB-hist[2][i]), Scalar(0,0,255), thickness);
			}
			++i;
		}
		else
		{
			if (chanel == 1)
			{
				rectangle(m_showMat, cv::Point(10+2*(i-1), heightB),cv::Point(10+2*i, heightB-hist[0][i-1]),Scalar::all(255), thickness);
				rectangle(m_showMat, cv::Point(10+2*i, heightB),cv::Point(10+2*(i+1), heightB-hist[0][i]),Scalar::all(255), thickness);
			}
			else
			{
				rectangle(m_showMat, cv::Point(10+2*(i-1), heightS),cv::Point(10+2*i, heightS-hist[0][i-1]),Scalar(255,0,0), thickness);
				rectangle(m_showMat, cv::Point(10+2*i, heightS),cv::Point(10+2*(i+1), heightS-hist[0][i]),Scalar(255,0,0), thickness);

				rectangle(m_showMat, cv::Point(10+2*(i-1), heightB),cv::Point(10+2*i, heightB-hist[1][i-1]),Scalar(0,255,0), thickness);
				rectangle(m_showMat, cv::Point(10+2*i, heightB),cv::Point(10+2*(i+1), heightB-hist[1][i]),Scalar(0,255,0), thickness);

				rectangle(m_showMat, cv::Point(10+2*(i-1), heightS3),cv::Point(10+2*i, heightS3-hist[2][i-1]),Scalar(0,0,255), thickness);
				rectangle(m_showMat, cv::Point(10+2*i, heightS3),cv::Point(10+2*(i+1), heightS3-hist[2][i]),Scalar(0,0,255), thickness);
			}
			i+=2;
		}
	}

	imshow(wndname, m_showMat);

	if(bSave)
	{
		if(savename.empty())
			savename = "default.bmp";
		imwrite(savename, m_showMat);
	}

	return true;
}

//equalize image hist and give the changed image;
Mat & section_3_3::equalizeImgHist(Mat &src)
{
	src.copyTo(m_destMat);

	int channel = src.channels();
	float k = 255.f/src.total();

	//calculate hist;
	vector< vector<int> > hist;
	getImgHist(src, hist, channel);

	//create map table;
	vector<uchar> pixtable(256, 0);
	vector< vector<uchar> > ttable;
	for(int i=0; i<channel; ++i)
	{
		size_t accum = 0;
		for (size_t j=0; j<pixtable.size(); ++j)
		{
			accum += hist[i][j];
			pixtable[j] = static_cast<uchar>(k*accum);
		}
		ttable.push_back(pixtable);
	}

	//equalize src image;
	for (int i=0; i<src.rows; ++i)
	{
		uchar *src_ptr = src.ptr<uchar>(i);
		uchar *dest_ptr = m_destMat.ptr<uchar>(i);
		for (int j=0; j<src.cols; ++j)
		{
			if(channel==1)
			{
				dest_ptr[j] = ttable[0][src_ptr[j]];
			}
			else
			{
				int j3 = j*3;
				dest_ptr[j3] = ttable[0][src_ptr[j3]];
				dest_ptr[j3+1] = ttable[1][src_ptr[j3+1]];
				dest_ptr[j3+2] = ttable[2][src_ptr[j3+2]];
			}
		}
	}

	return m_destMat;
}

//hist matching---see <<digital image processing-3E-chs>>-section3.3.2,page:77-83;
//hist_graph--histogram curve given by multiplie points;
Mat& section_3_3::histMatching(Mat &src, vector<cv::Point> &hist_graph)
{
	if(src.channels()!=1 || hist_graph.size()<2 || hist_graph[0].x!=0||hist_graph[hist_graph.size()-1].x!=255)
		return m_destMat;
	//check each point--every point's x coord should not less than the last one;
	for (size_t i=1; i<hist_graph.size(); ++i)
	{
		if(hist_graph[i].x<hist_graph[i-1].x)
			return m_destMat;
	}
	//create dest mat;
	src.copyTo(m_destMat);

	//equalize src image;
	float k = 255.f/src.total();
	//calculate hist;
	vector< vector<int> > src_hist;
	getImgHist(src, src_hist, 1);

	//create src equalize table--index is r, value is s;
	uchar r_s_table[256] = {0};
	size_t accum = 0;
	for (size_t i=0; i<256; ++i)
	{
		accum += src_hist[0][i];
		r_s_table[i] = static_cast<uchar>(k*accum);
	}

	//create the required hist;
	int hist[256]={0};
	int uplimit = min(256, hist_graph[1].x);
	size_t j=0;
	size_t total_pix = 0;
	for(int i=0;;)
	{
		float k = (hist_graph[j+1].y-hist_graph[j].y)/(float)(hist_graph[j+1].x-hist_graph[j].x);

		for(; i<=uplimit; ++i)
		{
			int pix = 0;
			//not vertical
			if(hist_graph[j].x < hist_graph[j+1].x)
			{
				pix = saturate_cast<int>(hist_graph[j].y+k*(i-hist_graph[j].x));
			}
			else
			{//vertical
				pix = hist_graph[j].y;
			}
			hist[i] = pix;
			total_pix += pix;
		}
		//shift the section and check
		if (++j < hist_graph.size()-1)
		{
			uplimit = hist_graph[j+1].x;
		}
		else
		{
			for (;i<256; ++i)
			{
				hist[i] = 0;
			}
			break;
		}
	}

	//create dest equalize table--index is z, value is s;
	k = 255.f/total_pix;
	uchar z_s_table[256] = {0};
	accum = 0;
	for (size_t i=0; i<256; ++i)
	{
		accum += hist[i];
		z_s_table[i] = static_cast<uchar>(k*accum);
	}
	
//	cout<<"total="<<total_pix<<",accum="<<accum<<endl;

	//create s_z table,init to -1 --index is s, value is z;
	int s_z_table[256];
	for(int t=0; t<256; ++t)
		s_z_table[t] = -1;
	//from big to small--if multiple z mapped to same s, the big overlayed by small z;
	for (int i=255; i>-1; --i)
	{
		s_z_table[z_s_table[i]] = i;
//		cout<<i<<"-"<<(int)z_s_table[i]<<endl;
	}

	//create the final transform table--index is r, value is z;
	uchar r_z_table[256]={0};
	uchar last_z = 0;
	for (int i=0; i<256; ++i)
	{
		if (s_z_table[i] != -1)
		{
			last_z = r_z_table[i] = r_s_table[s_z_table[i]];
		}
		else
		{
			r_z_table[i] = last_z;
		}
//		cout<<i<<"-"<<(int)r_z_table[i]<<endl;
	}

	//tranform the src image pixels value;
	for (int i=0; i<src.rows; ++i)
	{
		uchar *src_ptr = src.ptr<uchar>(i);
		uchar *dest_ptr = m_destMat.ptr<uchar>(i);
		for (int j=0; j<src.cols; ++j)
		{
			dest_ptr[j] = r_z_table[src_ptr[j]];
		}
	}

	return m_destMat;
}

//match the image hist to the given hist, most codes are same with the one above;
Mat& section_3_3::histMatching(Mat &src, vector<int> &dest_hist)
{
	if(src.channels()!=1 || dest_hist.size()!=256)
		return m_destMat;

	//create dest mat;
	src.copyTo(m_destMat);

	//equalize src image;
	float k = 255.f/src.total();
	//calculate hist;
	vector< vector<int> > src_hist;
	getImgHist(src, src_hist, 1);

	//create src equalize table--index is r, value is s;
	uchar r_s_table[256] = {0};
	size_t accum = 0;
	for (size_t i=0; i<256; ++i)
	{
		accum += src_hist[0][i];
		r_s_table[i] = static_cast<uchar>(k*accum);
	}

	//create dest equalize table--index is z, value is s;
	size_t total = 0;
	for(int i=0; i<256; ++i)
		total += dest_hist[i];
	k = 255.f/total;
	uchar z_s_table[256] = {0};
	accum = 0;
	for (size_t i=0; i<256; ++i)
	{
		accum += dest_hist[i];
		z_s_table[i] = static_cast<uchar>(k*accum);
	}

	//create s_z table,init to -1 --index is s, value is z;
	int s_z_table[256];
	for(int t=0; t<256; ++t)
		s_z_table[t] = -1;
	//from big to small--if multiple z mapped to same s, the big overlayed by small z;
	for (int i=255; i>-1; --i)
	{
		s_z_table[z_s_table[i]] = i;
		//		cout<<i<<"-"<<(int)z_s_table[i]<<endl;
	}

	//create the final transform table--index is r, value is z;
	uchar r_z_table[256]={0};
	uchar last_z = 0;
	for (int i=0; i<256; ++i)
	{
		if (s_z_table[i] != -1)
		{
			last_z = r_z_table[i] = r_s_table[s_z_table[i]];
		}
		else
		{
			r_z_table[i] = last_z;
		}
// 		cout<<i<<"-"<<(int)r_z_table[i]<<endl;
	}

	//tranform the src image pixels value;
	for (int i=0; i<src.rows; ++i)
	{
		uchar *src_ptr = src.ptr<uchar>(i);
		uchar *dest_ptr = m_destMat.ptr<uchar>(i);
		for (int j=0; j<src.cols; ++j)
		{
			dest_ptr[j] = r_z_table[src_ptr[j]];
		}
	}
	return m_destMat;
}

//use local hist to equalize image hist;--see<<digital image processing>>(call it DIP from now on),section 3.3.3;
//note! rgSize should less than 17, and this function is slow if rgSize is big!!
Mat& section_3_3::localEqualize(Mat &src, int rgSize)
{
	//only deals odds that less than 17;
	//if rgSize>16 then transtable will bigger than 256, this method will have no attraction;
	if (src.channels()!=1 || (rgSize%2 != 1) || rgSize>15)
		return m_destMat;
	/*
	the equalize formula:	Sk=((L-1)/M*N)*sigma_i=0~k(Ri);
	to this M*N=rgSize*rgSize, L=256, and i at most can be  numbers,others are 0, so this formula equals(take 3*3 as example):
	Sk = a*i; a=255/9, i=1~9; so we can create a table to accelerate the speed;
	*/
	int tabSize = rgSize*rgSize;
	uchar *trans_table = new uchar[tabSize];
	memset(trans_table, 0, tabSize);
	float k = 255.f/tabSize;
	for (int i=0; i<tabSize; ++i)
	{	
		trans_table[i] = static_cast<uchar>(k*(i+1));
// 		cout<<i<<"="<<int(trans_table[i])<<endl;
	}
	//create the dest mat;
	src.copyTo(m_destMat);

	//
	int *box = new int[rgSize];
	for (int i=0; i<rgSize; ++i)
	{
		box[i] = -rgSize/2+i;
	}
	int step = src.step[0];
	int offset_i=0,offset_j=0;
	int count=0;
	for (int i=0; i<src.rows; ++i)
	{
		for (int j=0; j<src.cols; ++j)
		{
			uchar pix = *(src.data+i*step+j);
			count = 0;

			for (int a=0; a<rgSize; ++a)
			{
				for (int b=0;b<rgSize; ++b)
				{
					offset_i = std::min(std::max(0,i+box[a]), src.rows-1);
					offset_j = std::min(std::max(0,j+box[b]), src.cols-1);

					if(*(src.data+step*offset_i+offset_j) <= pix)
						++count;
				}
			}
			*(m_destMat.data+i*step+j) = trans_table[count];
		}
	}

	return m_destMat;
}

//image enhancement use local hist, <DIP>section 3.3.4;
Mat& section_3_3::enhanceImg(Mat &src, int rgSize, float E, float k0, float k1, float k2)
{
	if (src.channels()!=1 || (rgSize !=3 && rgSize != 5))
		return m_destMat;
	//get histogram of total image;
	vector< vector<int> > hist;
	getImgHist(src, hist, 1);
	//calculate average
	vector<float> pers(256,0.f);
	float m_g = 0.f;
	float totals = src.total();
	for (int i=0; i<256; ++i)
	{
		pers[i] = hist[0][i]/totals;
		m_g += i*pers[i];
	}
	//calculate sigma2
	float sigma2_g = 0.f;
	for (int i=0; i<256; ++i)
	{
		sigma2_g += (i-m_g)*(i-m_g)*pers[i];
	}
	
	cout<<"mg="<<m_g<<",sigma2_g="<<sigma2_g<<endl;

	//create the dest mat;
	src.copyTo(m_destMat);

	//roi offsets
	int *box = new int[rgSize];
	for (int i=0; i<rgSize; ++i)
	{
		box[i] = -rgSize/2+i;
	}
	//prepare vars	
	int roiSize = rgSize*rgSize;
	int step = src.step[0];
	int offset_i=0,offset_j=0;
	float count = 0;
	float k0_mg = k0*m_g;
	float k1_sigma2_g = k1*sigma2_g;
	float k2_sigma2_g = k2*sigma2_g;

	//caluc and assign
	for (int i=0; i<src.rows; ++i)
	{
		for (int j=0; j<src.cols; ++j)
		{
			uchar pix = *(src.data+i*step+j);
			count = 0.f;
			//calculate local avg;
			for (int a=0; a<rgSize; ++a)
			{
				for (int b=0;b<rgSize; ++b)
				{
					offset_i = std::min(std::max(0,i+box[a]), src.rows-1);
					offset_j = std::min(std::max(0,j+box[b]), src.cols-1);

					count += *(src.data+step*offset_i+offset_j);
				}
			}
			float m = count/roiSize;
			float sigma2 = 0.f;
			//calculate local sigma2;
			for (int a=0; a<rgSize; ++a)
			{
				for (int b=0;b<rgSize; ++b)
				{
					offset_i = std::min(std::max(0,i+box[a]), src.rows-1);
					offset_j = std::min(std::max(0,j+box[b]), src.cols-1);
					
					uchar vl = *(src.data+step*offset_i+offset_j);
					sigma2 += (vl-m)*(vl-m);
				}
			}
			sigma2 /=roiSize;
			
			//check conditions;
			if(m <= k0_mg && sigma2<=k2_sigma2_g && sigma2>=k1_sigma2_g)
				*(m_destMat.data+i*step+j) = MIN(saturate_cast<uchar>(E*pix), 255);
			else
				*(m_destMat.data+i*step+j) = pix;

			if(0 && i>230&&j>190&&i<270&&j<230)
				cout<<j<<","<<i<<",m="<<m<<",sigma="<<sigma2<<endl;

		}
	}

	return m_destMat;
}


 

对应声明,按顺序来的。贴几个处理的图像:

原始的dog图像,以lena直方图为目标直方图变换后的图像。

对应的变换前,变换后,以及lena的直方图--从直方图可以看出,变换后的直方图确实与lena的很相似:

然后是局部直方图均衡化,对lena图像使用7*7邻域;

最后是直方图用于图像增强,对钨丝图像,采用的参数为邻域为3*3,E=4,k0=0.35,k1=0.0001,k2=0.05,下面是变换前后的图

虽然黑白交界处出现了轮廓,但是对右边很暗的那部分,确实出现了与书中一样的效果。

继续努力,下一个section!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值