目录
1.问题描述
数字图像处理是一门很有意思的学问,在现实生活中往往一个很简单的问题在数字图像中有时会非常复杂,旋转便是一类非常有意思的问题。如何在离散图像中高精度、快速求解图像的旋转角度,这个问题我思考了很长时间,下面会使用三种不同的算法逐一计算轮廓的旋转。
如图1-1所示,这是一幅鸟的轮廓:
如果这副图像发生了旋转,会出现什么样的情况呢?如图1-2所示,绿色的线表示旋转后的轮廓,在这里我设置了旋转角度为50°,旋转中心为轮廓的形心。
这也就是说,给定白色和绿色的两条轮廓,我们需要解出它们的旋转角度(50°);
2.旋转的三种解法
2.1 应用迭代法进行求解
算法的基本参数:
1.给定轮廓相似程度的度量方式,这里我采用了ShapeContextDistance;
2.给定旋转的步长,也就是每次匹配时绿色轮廓旋转的变化量,这里为了实验方便步长为10°;
3.给定迭代的终止条件,这里为了快速迭代,迭代次数为10;
如何迭代:
每次旋转后计算ShapeContextDistance,达到阈值或者达到迭代上限即跳出循环。
code:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
//计算图像上所有的轮廓,并计算它的质心和对角线长度
void FindBlobs(Mat img, vector<vector<Point>> &contours,
vector<Point2f> &MassCentre, vector<float>&DiagonalLength)
{
//判断图像是否为8位单通道
if (img.empty() || img.depth() != CV_8UC1)
{
cout << "Invalid Input Image!";
exit(-1);
}
//计算轮廓
vector<Vec4i> hierarchy;
findContours(img, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//计算轮廓的最小外接矩形
vector<Rect> boundRect(contours.size());
vector<Moments> mu(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{
//计算外界矩形
boundRect[i] = boundingRect(Mat(contours[i]));
//计算轮廓矩
mu[i] = moments(contours[i], false);
//当binaryImage=true时,所有的非零值都视为1
//计算质心
MassCentre.push_back(Point2f(static_cast<float>(mu[i].m10 / mu[i].m00),
static_cast<float>(mu[i].m01 / mu[i].m00)));
//计算外接矩形的对角距离
DiagonalLength.push_back((float)norm(boundRect[i].tl()
- boundRect[i].br()));
}
}
//对轮廓进行平移变化
void TransformContour(vector<vector<Point>> contours,
vector<vector<Point>> &contours_Trans, Vec2f Translate)
{
//判断
if (contours.size() == 0)
{
cout << "Invalid input!";
exit(-1);
}
//平移变换
contours_Trans = contours;
for (size_t i = 0; i < contours.size(); i++)
{
for (int idx = 0; idx < contours[i].size(); idx++)
{
contours_Trans[i][idx] = Point(contours[i][idx].x
+ Translate(0), contours[i][idx].y + Translate(1));
}
}
}
//旋转轮廓
//1.基于仿射变化的2阶方阵M计算旋转位置
Point RotatePoint(const Mat &M, const Point &p)
{
Point2f rp;
rp.x = (float)(M.at<double>(0, 0)*p.x + M.at<double>(0, 1)*p.y
+ M.at<double>(0, 2));
rp.y = (float)(M.at<double>(1, 0)*p.x + M.at<double>(1, 1)*p.y
+ M.at<double>(1, 2));
return rp;
}
//2.计算选择轮廓
void RotateContour(vector<vector<Point>> contours,
vector<vector<Point>> &contours_Rotated, double Angle, Point2f Centre)
{
//判断
if (contours.size() == 0)
{
cout << "Invalid input!";
exit(-1);
}
//计算仿射矩阵
Mat M= getRotationMatrix2D(Centre, Angle, 1.0);
//计算旋转轮廓
contours_Rotated = contours;
for (size_t i = 0; i < contours.size(); i++)
{
for (int idx = 0; idx < contours[i].size(); idx++)
{
contours_Rotated[i][idx] = RotatePoint(M,