图像特征之傅里叶描述子

使用C++、opencv获取轮廓的傅里叶描述子

傅里叶描述子是一种图像特征,具体来说,是一个用来描述轮廓的特征参数。其基本思想是用物体边界信息的傅里叶变换作为形状特征,将轮廓特征从空间域变换到频域内,,提取频域信息作为图像的特征向量。即用一个向量代表一个轮廓,将轮廓数字化,从而能更好地区分不同的轮廓,进而达到识别物体的目的。

关于傅里叶描述子的概述可参考论文(http://www.doc88.com/p-7176387138708.html)的2.3节。

在冈萨雷斯的《数字图象处理》一书中介绍了傅里叶描述子的详细原理:

总结:傅立叶描述子可以很好地描述轮廓特征,并且只需少量的描述子(即向量中的数不需要太多)即可大致代表整个轮廓。其次,对傅立叶描述字进行简单的归一化操作后,即可使描述子具有平移、旋转、尺度不变性,即不受轮廓在图像中的位置、角度及轮廓的缩放等影响,是一个鲁棒性较好的图像特征。

注:代码适用于物体已大致分割出来,并且图像中只存在1个目标物体的情况,其他情况需要视需求改代码。

#include "stdafx.h"
#include <opencv2/opencv.hpp>  
#include <iostream>  
#include <math.h> 
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;
using namespace std;

int main()
{
	//读取图像
	Mat src_image = imread("D:\\4.PNG");
	//图像读取出错处理
	if (!src_image.data)
	{
		cout << "src image load failed!" << endl;
		return -1;
	}
	//显示源图像
	namedWindow("原图", WINDOW_NORMAL);
	imshow("原图", src_image);

	//此处高斯去燥有助于后面二值化处理的效果
	//Mat blur_image;
	//GaussianBlur(src_image, blur_image, Size(15, 15), 0, 0);
	//imshow("GaussianBlur", blur_image);

	/*灰度变换与二值化*/
	Mat gray_image, binary_image;
	cvtColor(src_image, gray_image, COLOR_BGR2GRAY);
	threshold(gray_image, binary_image, 30, 255, THRESH_BINARY | THRESH_TRIANGLE);
	imshow("binary", binary_image);

	/*形态学闭操作*/
	Mat morph_image;
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	morphologyEx(binary_image, morph_image, MORPH_CLOSE, kernel, Point(-1, -1), 2);
	imshow("morphology", morph_image);

	/*查找外轮廓*/
	vector< vector<Point> > contours;
	vector<Vec4i> hireachy;
	findContours(binary_image, contours, hireachy, CV_RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());

	int l;//目标轮廓索引
	//寻找最大轮廓,即目标轮廓
	for (size_t t = 0; t < contours.size(); t++)
	{
		/*过滤掉小的干扰轮廓*/
		Rect rect = boundingRect(contours[t]);
		if (rect.width < src_image.cols / 2)
			continue;
		//if (rect.width >(src_image.cols - 20))
		l = t;//找到了目标轮廓,获取轮廓的索引
	}
	//画出目标轮廓
	Mat result_image = Mat::zeros(src_image.size(), CV_8UC3);
	vector< vector<Point> > draw_contours;
	draw_contours.push_back(contours[l]);
	drawContours(result_image, draw_contours, -1, Scalar(255,255,255), 1, 8, hireachy);
	namedWindow("lunkuo", WINDOW_NORMAL);
	imshow("lunkuo", result_image);

	//计算轮廓的傅里叶描述子
	Point p;
	int x, y, s;
	int i = 0,j = 0,u=0;
	s = (int)contours[l].size();
	Mat src1(Size(s,1),CV_8SC2);
	float f[9000];//轮廓的实际描述子
	float fd[16];//归一化后的描述子,并取前15个
	for (u = 0; u < s; u++)
	{
		float sumx=0, sumy=0;
		for (j = 0; j < s; j++)
		{
			p = contours[l].at(j);
			x = p.x;
			y = p.y;
			sumx += (float)(x*cos(2*CV_PI*u*j/s) + y*sin(2 * CV_PI*u*j / s));
			sumy+= (float)(y*cos(2 * CV_PI*u*j / s) - x*sin(2 * CV_PI*u*j / s));
		}
		src1.at<Vec2b>(0, u)[0] = sumx;
		src1.at<Vec2b>(0, u)[1] = sumy;
		f[u] = sqrt((sumx*sumx)+(sumy*sumy));
	}
	//傅立叶描述字的归一化
	f[0] = 0;
	fd[0] = 0;
	for (int k = 2; k < 17; k++)
	{
		f[k] = f[k] / f[1];
		fd[k - 1] = f[k];
		cout << fd[k-1] << endl;
	}
	//保存数据
	for (int k = 0; k < 16; k++)
	{
		FILE *fp = fopen("1.txt", "a");
		fprintf(fp, "%8f\t", fd[k]);
        fclose(fp);
	}
	FILE *fp = fopen("1.txt", "a");
	fprintf(fp, "\n");
	fclose(fp);
	waitKey();
	return 0;
}

源图像:

二值化图像、轮廓图:

将源图像进行旋转、放大操作后的图像:

二值化图像、轮廓图:

两次得到的傅里叶描述子向量如下所示:

可以看到两次得到的向量的对应元素值还是很相近的。

猜你喜欢

转载自blog.csdn.net/Lemon_jay/article/details/89349006