网站首页 > 文章精选 正文
使用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;
}
源图像:
?
二值化图像、轮廓图:
?
?
将源图像进行旋转、放大操作后的图像:
?
二值化图像、轮廓图:
?
?
两次得到的傅里叶描述子向量如下所示:
?
可以看到两次得到的向量的对应元素值还是很相近的。
- 上一篇: C#使用 OpenCvSharp 计算每个轮廓面积的指南
- 下一篇: OpenCV 图像分割
猜你喜欢
- 2025-01-01 前端智能化实践:从图片识别UI样式
- 2025-01-01 OpenCV 和 Python 识别数字的结果是怎样的呢
- 2025-01-01 HALCON_极坐标变换
- 2025-01-01 python使用fitz和opencv库提取pdf中的表格
- 2025-01-01 Fluent 多孔介质仿真(Porous Media)
- 2025-01-01 基于密度(Density-based)的聚类——核密度估计(KDE)
- 2025-01-01 机器视觉halcon学习系列---XLD的介绍和使用
- 2025-01-01 平学(26):Matlab学习之三维曲面图与常见函数(2)
- 2025-01-01 [OpenCV实战]13 OpenCV中使用Mask R-CNN进行对象检测和实例分割
- 2025-01-01 OpenCV使用分水岭算法实现图像分割
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)