输入图像 ->
阈值化 ->
检测到的旋转矩形边界框 ->
掩码
Contour area: 17719.0
Mask area: 20603.0
Compared area percentage: 86.002%
It is a rectangle!
另一张图片的处理流程如下:
输入图像 ->
阈值化 ->
检测旋转矩形边界框 ->
掩膜
Contour area: 13395.5
Mask area: 19274.5
Compared area percentage: 69.499%
It is not a rectangle!
import cv2
import numpy as np
# Load image, convert to grayscale, Otsu's threshold for binary image
image = cv2.imread('1.png')
mask = np.zeros(image.shape, dtype=np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Find contours, find rotated rectangle, find contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
contour_area = cv2.contourArea(cnts[0])
print('Contour area: {}'.format(contour_area))
rect = cv2.minAreaRect(cnts[0])
box = np.int0(cv2.boxPoints(rect))
cv2.drawContours(image, [box], 0, (36,255,12), 3)
# Find area of rotated bounding box and draw onto mask image
mask_area = cv2.contourArea(box)
cv2.drawContours(mask, [box], 0, (255,255,255), -1)
print('Mask area: {}'.format(mask_area))
# Compare areas and calculate percentage
rectangular_threshold = 80
percentage = (contour_area / mask_area) * 100
print('Compared area percentage: {:.3f}%'.format(percentage))
if percentage > rectangular_threshold:
print('It is a rectangle!')
else:
print('It is not a rectangle!')
# Display
cv2.imshow('image', image)
cv2.imshow('thresh', thresh)
cv2.imshow('mask', mask)
cv2.waitKey()
这与其中一个评论(边界框和检查内部区域)非常相似。我建议您使用面积和周长的方程来制作更强大且可配置的形状测试器。
-获取所需形状周围的适合边界框。
-检查面积:斑点像素计数应大致等于A(斑点):L * W(旋转边界框)
-检查周长:运行边缘变换(如Canny或等效物),并将边缘像素数量与矩形周长方程进行比较:P(斑点):2W + 2H(旋转边界框)
-在边缘图像上运行霍夫线,并检查正交性,这可能是另一个要考虑的适应度估计器,如果这是您关心的重要参数。
基本上,每当您检查形状健身时,通常希望检查基于您关心的形状的各种适应度估计器(例如清晰的边缘,垂直的角落,内部填充,直边等),并且您会对每个适应度估计器进行加权或限制,以创建所需的组合特异性。
编辑:(代码和其他说明)。因此,为了清楚起见,对于您的示例,我同意面积是最强的适应度估计。但是,在形状识别中通常有用的是跟踪多个适应度估计器,以便您可以真正调整您关心和不关心的内容。我在您的样本中添加了一个附加的(完全虚构的)形状,以演示仅使用面积可能不太可靠的情况(在干净的矩形中切割)。如您所见,面积对于突出部等外部形状错误非常敏感,但是周长对于内部形状错误更敏感。(再次,这可能或可能不重要,但我认为它很有趣,可能会有所帮助)。我添加了一个额外的参数(周长与面积比),这是一种用于估计形状复杂性的巧妙指标(特别是在叶子中)。
代码:
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <Windows.h>
#include <string>
using namespace std;
using namespace cv;
void drawOrientedBoundingBox(Mat img, RotatedRect obb, Scalar color)
{
Point2f vertices[4];
obb.points(vertices);
for (int i = 0; i < 4; i++)
line(img, vertices[i], vertices[(i + 1) % 4], color, 2);
}
int main(int argc, char** argv)
{
//play with these to dial in what you care about (across multiple sample images)
double areaFitHigh = 1.2;
double areaFitLow = 0.8;
double perimFitHigh = 1.05;
double perimFitLow = 0.85;
std::string fileName = "C:/Local Software/voyDICOM/resources/images/RectTester.jpg";
Mat tempImage = imread(fileName, cv::IMREAD_GRAYSCALE);
Mat bwImg;
cv::threshold(tempImage, bwImg, 0, 255, cv::THRESH_OTSU);
Mat contImg = Mat(bwImg.rows, bwImg.cols, CV_8UC3, Scalar(0, 0, 0));
vector<vector<Point> > contours;
findContours(bwImg, contours, RETR_LIST, CHAIN_APPROX_NONE);
bool areaFitnessFlag = false;
bool perimFitnessFlag = false;
for (size_t i = 0; i < contours.size(); i++)
{
std::string contourName = "S_" + std::to_string(i);
std::cout << "-------------Contour Detected------------" << std::endl;
std::cout << contourName << std::endl;
if (contours[i].size() >= 2 * bwImg.cols + 2 * bwImg.rows - 4)
{
std::cout << "image boundary... don't know how to make findContours not detect this up front" << std::endl;
continue;
}
RotatedRect obb = minAreaRect(contours[i]);
//draw graphics for debug purposes
drawOrientedBoundingBox(contImg, obb, Scalar(255, 0, 0));
drawContours(contImg, contours, static_cast<int>(i), Scalar(0, 0, 255), 2);
putText(contImg, contourName, obb.center, cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 255, 0), 1, false);
//perform fitness checks
double areaBlob = contourArea(contours[i]);
double areaOBB = obb.size.area();
double perimeterBlob = contours[i].size();
double perimeterOBB = 2 * obb.size.width + 2 * obb.size.height;
double perimToArea = 0;
if (areaBlob > 0) { perimToArea = perimeterBlob / areaBlob; }
std::cout << "area: " << areaBlob << " , " << areaOBB << std::endl;
std::cout << "perimeter: " << perimeterBlob << " , " << perimeterOBB << std::endl;
std::cout << "Perimeter to Area Ratio: " << perimToArea << std::endl;
double areaFitness = 0;
if (areaOBB > 0) { areaFitness = areaBlob / areaOBB; }
std::cout << "Area Fitness: " << areaFitness << std::endl;
double perimeterFitness = 0;
if (perimeterOBB > 0) { perimeterFitness = perimeterBlob / perimeterOBB; }
std::cout << "Perimeter Fitness: " << perimeterFitness << std::endl;
if (areaFitness > areaFitHigh || areaFitness < areaFitLow)
{ areaFitnessFlag = false; }
else
{ areaFitnessFlag = true; }
if (perimeterFitness > perimFitHigh || perimeterFitness < perimFitLow)
{ perimFitnessFlag = false; }
else
{ perimFitnessFlag = true; }
if (areaFitnessFlag && perimFitnessFlag)
{ std::cout << "This is a rectangle!" << std::endl; }
else
{ std::cout << "This is not a rectangle..." << std::endl; }
}
namedWindow("Original", WINDOW_AUTOSIZE);
imshow("Original", tempImage);
namedWindow("Thresh", WINDOW_AUTOSIZE);
imshow("Thresh", bwImg);
namedWindow("Contours", WINDOW_AUTOSIZE);
imshow("Contours", contImg);
waitKey(0);
system("pause");
return 0;
}
结果图片:
有用链接:
https://docs.opencv.org/master/df/dee/samples_2cpp_2minarea_8cpp-example.html
https://docs.opencv.org/master/db/dd6/classcv_1_1RotatedRect.html
https://learnopencv.com/contour-detection-using-opencv-python-c/
A = L * W
? - nathancy