在机器视觉领域,经常会遇到圆环型的检测目标,比如瓶口,轮胎,橡皮圈等等,比如想检测轮胎上的瑕疵,就可以通过把环形区域展开成矩形形状,然后对胎侧进行瑕疵检测,再比如对圆环扣上的字符进行识别,也可以利用该方法,当然也可以直接利用弯曲文本标注进行识别。那么具体如何实现环形区域展开成矩形长条呢?代码如下:
算法实现代码:
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/imgproc/types_c.h>
#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
cv::Mat UTCH::Method::CircleToRectangle(const cv::Mat& circle, const cv::Point& Center, int Radius, int RingStride)
{
cv::Mat rectangle;
rectangle = cv::Mat::zeros(cv::Size(Radius * CV_PI * 2, RingStride), CV_8UC1);//矩形长为
int nl = rectangle.rows; // number of lines
int nc = rectangle.cols * rectangle.channels(); // total number of elements per line
for (int j = 0; j < nl; j++) {
// get the address of row j
uchar* data = rectangle.ptr<uchar>(j);
for (int i = 0; i < nc; i++) {
// process each pixel ---------------------
double theta = CV_PI * 2.0 / float(nc) * float(i + 1);
double rho = Radius - j - 1; //-1防止超界
int position_x = (float)Center.x + rho * (float)std::cos(theta) + 0.5;// +0.5四舍五入
int position_y = (float)Center.y - rho * (float)std::sin(theta) + 0.5;// +0.5四舍五入
data[i] = circle.at<uchar>(position_y, position_x);
// end of pixel processing ----------------
} // end of line
}
return rectangle;
}
主函数代码:
#include "myalgrithom.h"
#include <iostream>
#include<windows.h>
using namespace std;
int main()
{
//读取彩色图像
std::string strImgFile = "image/yinzhang.jpg";
cv::Mat img = cv::imread(strImgFile);
if (img.empty())
{
std::cout << "image file read failed - " << strImgFile.c_str() << "!" << std::endl;
return 0;
}
//转换灰度图
cv::Mat gray;
cvtColor(img, gray, cv::COLOR_BGR2GRAY);
//GaussianBlur(gray, gray, Size(5, 5), 1.0);
//检测最大的圆
std::vector<cv::Vec3f> circles;
HoughCircles(gray, circles, cv::HOUGH_GRADIENT, 1, 50, 50);//根据检测目标,参数自己调
cv::Mat img_copy;
img.copyTo(img_copy);
int nMaxRadius = 0;
cv::Point center;
for (int i = 0; i < circles.size(); i++)
{
cv::Vec3f cc = circles[i];//检测到圆的圆心和半径
if (nMaxRadius < cc[2])
{
nMaxRadius = int(cc[2] + 0.5);
center = cv::Point(cc[0], cc[1]);
}
//circle(img_copy, cv::Point(cc[0], cc[1]), cc[2], cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
//circle(img_copy, cv::Point(cc[0], cc[1]), 3, cv::Scalar(125, 25, 255), 2, cv::LINE_AA);
}
circle(img_copy, center, nMaxRadius, cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
circle(img_copy, center, 3, cv::Scalar(125, 25, 255), 2, cv::LINE_AA);
//将最大的圆环展平,圆环高度为半径的一半
cv::Mat result;
result=UTCH::Method::CircleToRectangle(gray, center, nMaxRadius, nMaxRadius / 2);
cv::flip(result, result,1);
if (result.empty())
{
std::cout << "result is empty!" << std::endl;
return 0;
}
else
{
//显示结果
imshow("result", result);
imwrite("result/result.jpg", result);
cv::waitKey(0);
cv::destroyAllWindows();
}
}
其中,输入为
Mat& circle:输入的圆环形原始图像
原图如下
检测到的圆形如下绿线圈出
Point Center: 原始图像中圆环的圆心
int Radius: 原始图像中圆环的半径
int RingStride:圆环的宽度
最终生成的矩形长条,以圆环的外周长为长,以圆环的宽度为高。
转换后结果图:
函数中,从圆环的最外圈开始遍历,最外圈即对应着矩形中的第一行,宽度为多少,则矩形中就有多少行
以圆心为中心构成极坐标系,则圆环上任意一点可以用rho和theta来表示
再根据公式计算出该点在图上的真是坐标,把像素值赋给矩形框中对应的位置。
注:代码并不是拷贝下来就能直接使用的哦,根据自己的情况去修改调试哦,加油宝子们。