图像通用特征的提取

18 篇文章 2 订阅
18 篇文章 0 订阅

主要包含了图像的灰度直方图、灰度图像的信息熵、圆形度……等35个图像通用特征的提取;

Character.h

#include <vector>
#include <iterator>
#include <numeric>
#include <algorithm>
#include <functional>

using namespace std;

// 存储目标物体的椭圆结构特征
typedef struct _FitEllipse
{
	float e;		// 离心率
	float height;	// 长轴长
	float width;	// 短轴长
	float fit;		// 拟合度
	float angle;	// 椭圆倾角
	float length;	// 椭圆周长
	float area;		// 椭圆面积

} FitEllipse;

// 存储三个 double 类型的数组
typedef struct _Scalar
{
public:
	inline _Scalar Scalar(double val0 = 0, double val1 = 0, double val2 = 0)
	{
		val[0] = val0;
		val[1] = val1;
		val[2] = val2;
	}
public:
	double val[3];

} Scalar;

class ImageProcess
{
public:
	ImageProcess();
	virtual ~ImageProcess();

public:
	// load image
	IplImage* loadImage(const char* fileName, unsigned colorMode = 1);
	// binaryzation of image
	IplImage* binaryImg(IplImage* img, unsigned threshold, int thresholdType);

public:
	// get binary image
	IplImage* getBinaryImg() { return m_binaryImg; }
	
private:
	IplImage* m_binaryImg;		// binary image
};

class Character : public ImageProcess
{
public:
	Character();
	~Character();

public:
	// 查找轮廓
	CvSeq* findImgContours(IplImage* img, CvMemStorage* storage);
	// 计算目标区域的均值和方差
 	void calAvgSdv(IplImage* img, CvSeq* seq, Scalar *ave = NULL, Scalar *sdv = NULL);

	// 计算直方图
	template<typename _T>
	void calHist(IplImage* img, CvSeq* seq, _T hist[]);
	// 计算概率最大的灰度值
	int calHistValue(IplImage* img, CvSeq* seq);
	// 计算图像的熵值
	double calEntropy(IplImage* img, CvSeq* seq);
	// 计算图像的椭圆拟合度
	FitEllipse calEllipseCharater(CvSeq* seq);
	// 计算矩形度
	double calRectFit(CvSeq* seq);
	// 计算紧密度
	double calHullFit(CvSeq* seq, CvMemStorage* storage = 0);
	// 计算目标区域面积
	int calROIArea(IplImage* img, CvSeq* seq);
	// 计算重心
	CvPoint calBaryCentre(IplImage* img, CvSeq* seq);
	// 计算形状参数(紧凑性)
	double calCompactness(CvSeq* seq);
	// 计算偏心率
	double calEccentricity(CvSeq* seq);
	// 计算圆形性
	double calCircularity(IplImage* img, CvSeq* seq);
	// 计算 7个 hu 矩
	CvHuMoments calHuMoments(CvSeq* seq);
	// 获取面积最大的轮廓
	CvSeq* getMaxAreaContour(CvSeq* seq);

private:
	CvSeq* m_seq;
	FitEllipse m_ellipse;
	int m_nArea;
	int m_nX;
	int m_nY;
};

characer.cpp

#include "stdafx.h"
#include "Character.h"

ImageProcess::ImageProcess()
	:m_binaryImg(NULL)
{

}

ImageProcess::~ImageProcess()
{
	if (m_binaryImg != NULL)
	{
		cvReleaseImage(&m_binaryImg);
		m_binaryImg = NULL;
	}
}

IplImage* ImageProcess::loadImage(const char* fileName, unsigned colorMode /* = 1 */)
{
	return cvLoadImage(fileName, colorMode);
}


IplImage* ImageProcess::binaryImg(IplImage* img, unsigned threshold, int thresholdType)
{
#ifdef _DEBUG
	assert(img != NULL);
	assert((img->nChannels == 1) || (img->nChannels == 3));
	assert((threshold >= 0) && (threshold <= 255));
#endif

	m_binaryImg = cvCreateImage(cvGetSize(img), img->depth, 1);

	if (img->nChannels == 1)
	{
		cvThreshold(img, m_binaryImg, threshold, 255, thresholdType);
	}
	else
	{
		IplImage* rImg = cvCreateImage(cvGetSize(img), img->depth, 1);
		cvSplit(img, NULL, NULL, rImg, NULL);
		cvThreshold(rImg, m_binaryImg, threshold, 255, thresholdType);

		if (rImg != NULL)
		{
			cvReleaseImage(&rImg);
		}
	}

	IplConvKernel* element = cvCreateStructuringElementEx(5, 5, 2, 2, CV_SHAPE_ELLIPSE);
	cvSmooth(m_binaryImg, m_binaryImg, CV_MEDIAN);
	cvErode(m_binaryImg, m_binaryImg, element, 1);
	cvDilate(m_binaryImg, m_binaryImg, element, 1);
	cvReleaseStructuringElement(&element);

	return m_binaryImg;

}

Character::Character()
	: m_nArea(0)
{

}

Character::~Character()
{

}

CvSeq* Character::getMaxAreaContour(CvSeq* seq)
{
#ifdef _DEBUG
	assert(seq != 0);
#endif

	CvSeq* maxSeq = 0;

	for (CvSeq* c = seq; c != NULL; c = c->h_next)
	{
		float area = fabs(cvContourArea(c, CV_WHOLE_SEQ));	// get area of contours

		if (m_nArea < area)
		{
			maxSeq = c;
		}
	}

	return maxSeq;
}

// 查找图像轮廓
CvSeq* Character::findImgContours(IplImage* img, CvMemStorage* storage)
{
	if (img->nChannels != 1)
		return NULL;

	CvSeq *seq = 0;
	cvFindContours(img, storage, &seq, sizeof(CvContour), 
		CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);	// 检索轮廓
	
	for (; seq != 0; seq = seq->h_next)
	{
		m_seq = getMaxAreaContour(seq);
	}

	return m_seq;
}

// 计算目标区域的均值和方差
void Character::calAvgSdv(IplImage* img, CvSeq* seq, Scalar *ave, Scalar *sdv)
{
	IplImage* bitImg = getBinaryImg();

	CvRect rect = { 0 };
	for (; seq != 0; seq = seq->h_next)
	{
		// 画出二值图像的轮廓
		cvDrawContours(bitImg, seq, cvScalarAll(255), cvScalarAll(255),
			-1, CV_FILLED, 8);
		rect = cvBoundingRect(seq, 0);
	}

	if (img->nChannels == 1)		// 如果是单通道图像
	{
		vector<double> vec;

		for (int i = rect.x; i < rect.x + rect.width; ++i)
		{
			for (int j = rect.y; j < rect.y + rect.height; ++j)
			{
				CvScalar s = cvGet2D(bitImg, j, i);
				if (s.val[0] == 255)
				{
					s = cvGet2D(img, j, i);
					vec.push_back(s.val[0]);
				}
			}

		} // end for i

		// 计算均值
		if (ave != NULL)
		{
			if (!vec.empty())
			{
				ave->val[0] = (double)(accumulate(vec.begin(), vec.end(), 0))
					/ vec.size();
			}
		}

		// 计算方差
		if (sdv != NULL)
		{
			if (! vec.empty())
			{
				double sum = 0.0;
				for_each(vec.begin(), vec.end(), [&](const double d)
				{
					sum += pow((d - ave->val[0]), 2);
				});
				sdv->val[0] = sqrt(sum / vec.size());
			}
			
			
		}

	}// end if img->nChannel == 1

	if (img->nChannels == 3)  // 如果图像为 3 通道图像
	{
		vector<double> vec[3];

		for (int i = 0; i < rect.x + rect.width; ++i)
		{
			for (int j = 0; j < rect.y + rect.height; ++j)
			{
				CvScalar s = cvGet2D(bitImg, j, i);
				if (s.val[0] == 255)
				{
					s = cvGet2D(img, j, i);
					vec[0].push_back(s.val[0]);		
					vec[1].push_back(s.val[1]);		
					vec[2].push_back(s.val[2]);		
				}
			}

		}// end for i

		// 计算均值
		if (ave != NULL)
		{
			for (int i = 0; i < 3; ++i)
			{
				if (! vec[i].empty())
				{
					ave->val[i] = (double)(accumulate(vec[i].begin(), vec[i].end(), 0))
						/ vec[i].size();
				}
			}
		}// end ave

		if (sdv != NULL)
		{
			for (int i = 0; i < 3; ++i)
			{
				if (!vec[i].empty())
				{
					double sum = 0.0;
					for_each(vec[i].begin(), vec[i].end(), [&](const double d)
					{
						sum += pow(d - ave->val[i], 2);
					});	

					sdv->val[i] = sqrt(sum / vec->size());
				}	
			}
		}// end if sdv

	}// end if img->nChannels == 3


}

// 计算直方图
template<typename _T>
void Character::calHist(IplImage* img, CvSeq* seq, _T hist[])
{
	IplImage* grayImg = NULL;
	IplImage* bitImg = getBinaryImg();
	grayImg = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);

	if (img->nChannels == 3)
		cvCvtColor(img, grayImg, CV_RGB2GRAY);
	else
		cvCopy(img, grayImg, NULL);

	CvRect rect = { 0 };

	for (; seq != 0; seq = seq->h_next)
	{
		cvDrawContours(bitImg, seq, cvScalarAll(255), cvScalarAll(255),
			-1, CV_FILLED, 8);
		rect = cvBoundingRect(seq, 0);
	}

	for (int i = rect.x; i < rect.x + rect.width; ++i)
	{
		for (int j = rect.y; j < rect.y + rect.height; ++j)
		{
			CvScalar s = cvGet2D(bitImg, j, i);	
			if (s.val[0] == 255)
			{
				s = cvGet2D(grayImg, j, i);
				int val = s.val[0];
				++hist[val];
			}
		}
	}

	// 释放内存
	cvReleaseImage(&grayImg);
}

// 利用直方图,统计出出现频率最高的灰度值
int Character::calHistValue(IplImage* img, CvSeq* seq)
{
	int hist[256] = { 0 };
	calHist(img, seq, hist);

	int maxVal = 0;
	int grayVal = 0;

	for (int i = 0; i < 256; ++i)
	{
		if (maxVal < hist[i])
		{
			maxVal = hist[i];
			grayVal = i;			// 求出出现概率最大的灰度值
		}
	}

	return grayVal;

}

// 计算图像的熵值
double Character::calEntropy(IplImage* img, CvSeq* seq)
{
	// 计算直方图
	double hist[256] = { 0.0 };
	calHist(img, seq, hist);

	int total = 0;
	for (int i = 0; i < 256; ++i)
	{
		total += hist[i];
	}

	// 计算每个灰度值的概率
	double temp[256] = { 0.0 };
	for (int i = 0; i < 256; ++i)
	{
		temp[i] = hist[i] / total;
	}

	// 计算信息熵
	double entropy = 0.0;
	for (int i = 0; i < 256; ++i)
	{
		if (temp[i] == 0.0)
		{
			entropy = entropy;
		}
		else
		{
			entropy -= temp[i] * (log(temp[i]) / log(2.0));
		}
	}
	
	return entropy;
}

// 提取椭圆特征
FitEllipse Character::calEllipseCharater(CvSeq* seq)
{
	CvBox2D box = { 0 };
	double area = 0;
	for (; seq != 0; seq = seq->h_next)
	{
		// 计算拟合椭圆
		box = cvFitEllipse2(seq);
		area = fabs(cvContourArea(seq, CV_WHOLE_SEQ));
	}
	
	box.angle = 90 - box.angle;
	m_ellipse.angle = box.angle;		// 倾角

	float height = box.size.height;		
	float width = box.size.width;

	// 取较大的值为椭圆的长轴长,较小的值为短轴长
	if (height > width)
	{
		m_ellipse.height = height;
		m_ellipse.width = width;
	}
	else
	{
		m_ellipse.height = width;
		m_ellipse.width = height;
	}

	float a = 0, b = 0;	
	a = m_ellipse.height;	// 长轴长
	b = m_ellipse.width;	// 短轴长

	// 周长
	float l = 3.14f * b + 2 * (a - b);
	m_ellipse.length = l;

	float ellArea = 3.14f * a * b / 4;		// 椭圆面积
	m_ellipse.area = ellArea;

	// 椭圆拟合度
	float fit = area / ellArea;
	m_ellipse.fit = fit;

	// 离心率
	float c = sqrt((a * a) - (b * b));
	float e = c / a;
	m_ellipse.e = e;

	return m_ellipse;
}

// 计算矩形度
double Character::calRectFit(CvSeq* seq)
{
	CvBox2D rect;
	double area = 0.0;
	for (; seq != 0; seq = seq->h_next)
	{
		rect = cvMinAreaRect2(seq);		// 最小拟合矩形
		area = fabs(cvContourArea(seq, CV_WHOLE_SEQ));
	}

	double rectArea = rect.size.height * rect.size.width;

	double fit = area / rectArea;	// 矩形度

	return fit;
}

// 计算凸包
double Character::calHullFit(CvSeq* seq, CvMemStorage* storage)
{
	CvSeq* hull = 0;
	double hullArea = 0.0, area = 0.0;

	for (; seq != 0; seq = seq->h_next)
	{
		// 最内外接凸多边形,多边形逼近
		//hull = cvApproxPoly(seq, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1);
		
		// 凸包,最小外接凸多边形
		hull = cvConvexHull2(seq, 0, CV_CLOCKWISE, 1);

		hullArea = fabs(cvContourArea(hull, CV_WHOLE_SEQ));
		area = fabs(cvContourArea(seq, CV_WHOLE_SEQ));
	}

	double fit = area / hullArea;

	return fit;
}

// 计算目标区域面积
int Character::calROIArea(IplImage* img,CvSeq* seq)
{
	if (img == NULL || img->nChannels != 1)
		return 0;
	
	CvRect rect = { 0 };
	for (; seq != 0; seq = seq->h_next)
	{
		cvDrawContours(img, seq, cvScalarAll(255), cvScalarAll(255), -1, CV_FILLED, 8);
		rect = cvBoundingRect(seq, 0);
	}

	for (int i = rect.x; i < rect.x + rect.width; ++i)
	{
		for (int j = rect.y; j < rect.y + rect.height; ++j)
		{
			CvScalar s = cvGet2D(img, j, i);
			if (s.val[0] == 255)
			{
				++m_nArea;
			}
		}
	}

	return m_nArea;

}

// 计算图像重心
CvPoint Character::calBaryCentre(IplImage* img,CvSeq* seq)
{
	if (img == NULL || img->nChannels != 1)
		return cvPoint(0, 0);
	
	
	double m00 = 0.0, m10 = 0.0, m01 = 0.0;

	for (; seq != 0; seq = seq->h_next)
	{
		CvMoments moment;
		cvMoments(img, &moment, 1);
		m00 = cvGetSpatialMoment(&moment, 0, 0);
		m10 = cvGetSpatialMoment(&moment, 1, 0);
		m01 = cvGetSpatialMoment(&moment, 0, 1);
	}

	// 图像重心坐标
	CvPoint pt = { 0 };
	pt.x =(int)(m10 / m00);
	pt.y =(int)(m01 / m00);

	return pt;
}

// 计算形状参数(紧密度)
double Character::calCompactness(CvSeq* seq)
{
	double area = 0, length = 0;
	for (; seq != 0; seq = seq->h_next)
	{
		area = fabs(cvContourArea(seq, CV_WHOLE_SEQ));
		length = cvArcLength(seq, CV_WHOLE_SEQ);
	}

	double f = (length * length) / (4 * 3.14 * area);

	return f;
}

// 计算偏心率
double Character::calEccentricity(CvSeq* seq)
{
	CvBox2D rect = { 0 };
	for (; seq != 0; seq = seq->h_next)
	{
		rect = cvMinAreaRect2(seq);		// 最小外接矩形
	}

	double f = 0.0;

	(rect.size.height > rect.size.width)
		? (f = rect.size.height / rect.size.width)
		: (f = rect.size.width / rect.size.height);

	return f;

}

// 计算圆形性
double Character::calCircularity(IplImage* img, CvSeq* seq)
{
	if (img == NULL || img->nChannels != 1)
		return 0.0;

	double m00 = 0.0, m10 = 0.0, m01 = 0.0;

	// 以下代码计算图像的圆形性
	float UR = 0.f;	// 从区域重心到边界点的平均距离
	float PR = 0.f;	// 从区域重心到边界点的距离的方差


	for (; seq != 0; seq = seq->h_next)
	{
		CvMoments moment;
		cvMoments(img, &moment, 1);
		m00 = cvGetSpatialMoment(&moment, 0, 0);
		m10 = cvGetSpatialMoment(&moment, 1, 0);
		m01 = cvGetSpatialMoment(&moment, 0, 1);

		// 图像重心坐标
		int x = (int)(m10 / m00);
		int y = (int)(m01 / m00);

		float sum = 0;
		int count = seq->total;
		CvPoint* pt;
		for (int i = 0; i < count; ++i)
		{
			pt = (CvPoint*)cvGetSeqElem(seq, i);
			sum += sqrt(powf(pt->x - x, 2) + powf(pt->y - y, 2));
		}

		UR = sum / count;

		float tmp = 0.f;
		sum = 0;
		for (int i = 0; i < count; ++i)
		{
			pt = (CvPoint*)cvGetSeqElem(seq, i);
			tmp = sqrt(powf(pt->x - x, 2) + powf(pt->y - y, 2));
			sum += powf(tmp - UR, 2);
		}

		PR = sum / count;

	}

	// 计算圆形性
	double circularity = UR / PR;
	
	return circularity;
}

// 计算 7 个 hu 不变矩

CvHuMoments Character::calHuMoments(CvSeq* seq)
{
	CvHuMoments huMoments;
	for (; seq != 0; seq = seq->h_next)
	{
		// 提取7个 hu 不变矩
		CvMoments moments;
		cvMoments(seq, &moments, 1);
		cvGetHuMoments(&moments, &huMoments);
	}

	return huMoments;
	
}



  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值