[英]Detecting lines and shapes in OpenCV using Python
我一直在玩OpenCV(cv2)並檢測線條和形狀。 假設我的女兒畫了一幅畫,就像這樣:
我正在嘗試編寫一個Python腳本來分析繪圖並將其轉換為硬線/形狀,如:
話雖這么說,我已經安裝了opencv並嘗試使用它,但除了能夠在圖像中繪制一條垂直線之外沒有運氣。 下面是我的代碼到目前為止,任何關於如何使用opencv進行此操作的指針或建議將不勝感激。
import cv2
import numpy as np
class File(object):
def __init__(self, filename):
self.filename = filename
def open(self, filename=None, mode='r'):
if filename is None:
filename = self.filename
return cv2.imread(filename), open(filename, mode)
def save(self, image=None, filename_override=None):
filename = "output/" + self.filename.split('/')[-1]
if filename_override:
filename = "output/" + filename_override
return cv2.imwrite(filename, image)
class Image(object):
def __init__(self, image):
self.image = image
def grayscale(self):
return cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
def edges(self):
return cv2.Canny(self.image, 0, 255)
def lines(self):
lines = cv2.HoughLinesP(self.image, 1, np.pi/2, 6, None, 50, 10)
for line in lines[0]:
pt1 = (line[0],line[1])
pt2 = (line[2],line[3])
cv2.line(self.image, pt1, pt2, (0,0,255), 2)
if __name__ == '__main__':
File = File('images/a.png')
Image = Image(File.open()[0])
Image.image = Image.grayscale()
Image.lines()
File.save(Image.image)
不幸的是,對於一個簡單的方形繪圖,我得到的全部是:
框中的垂直線是代碼的輸出。
這是我的嘗試。 它是在C ++中,但可以很容易地移植到python,因為大多數是OpenCV函數。
該方法的簡要概述,代碼中的注釋也應該有所幫助。
findContours
對於每個輪廓,獲得凸包(以處理開放的輪廓),並根據圓度進行分類。 處理每個形狀的方式不同
筆記:
更新 - 注意到在OpenCV 3.0.0中有minEnclosingTriangle函數。 這可能有助於使用而不是我的過程來找到三角形頂點。 但是,由於在代碼中插入此函數將是微不足道的,所以我將在代碼中保留我的過程,以防一個人沒有OpenCV 3.0.0。
編碼:
#include <opencv2\opencv.hpp>
#include <vector>
#include <iostream>
using namespace std;
using namespace cv;
/////////////////////////////////////////////////////////////////////////////////////////////
// Thinning algorithm from here:
// https://github.com/bsdnoobz/zhang-suen-thinning
/////////////////////////////////////////////////////////////////////////////////////////////
void thinningIteration(cv::Mat& img, int iter)
{
CV_Assert(img.channels() == 1);
CV_Assert(img.depth() != sizeof(uchar));
CV_Assert(img.rows > 3 && img.cols > 3);
cv::Mat marker = cv::Mat::zeros(img.size(), CV_8UC1);
int nRows = img.rows;
int nCols = img.cols;
if (img.isContinuous()) {
nCols *= nRows;
nRows = 1;
}
int x, y;
uchar *pAbove;
uchar *pCurr;
uchar *pBelow;
uchar *nw, *no, *ne; // north (pAbove)
uchar *we, *me, *ea;
uchar *sw, *so, *se; // south (pBelow)
uchar *pDst;
// initialize row pointers
pAbove = NULL;
pCurr = img.ptr<uchar>(0);
pBelow = img.ptr<uchar>(1);
for (y = 1; y < img.rows - 1; ++y) {
// shift the rows up by one
pAbove = pCurr;
pCurr = pBelow;
pBelow = img.ptr<uchar>(y + 1);
pDst = marker.ptr<uchar>(y);
// initialize col pointers
no = &(pAbove[0]);
ne = &(pAbove[1]);
me = &(pCurr[0]);
ea = &(pCurr[1]);
so = &(pBelow[0]);
se = &(pBelow[1]);
for (x = 1; x < img.cols - 1; ++x) {
// shift col pointers left by one (scan left to right)
nw = no;
no = ne;
ne = &(pAbove[x + 1]);
we = me;
me = ea;
ea = &(pCurr[x + 1]);
sw = so;
so = se;
se = &(pBelow[x + 1]);
int A = (*no == 0 && *ne == 1) + (*ne == 0 && *ea == 1) +
(*ea == 0 && *se == 1) + (*se == 0 && *so == 1) +
(*so == 0 && *sw == 1) + (*sw == 0 && *we == 1) +
(*we == 0 && *nw == 1) + (*nw == 0 && *no == 1);
int B = *no + *ne + *ea + *se + *so + *sw + *we + *nw;
int m1 = iter == 0 ? (*no * *ea * *so) : (*no * *ea * *we);
int m2 = iter == 0 ? (*ea * *so * *we) : (*no * *so * *we);
if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0)
pDst[x] = 1;
}
}
img &= ~marker;
}
void thinning(const cv::Mat& src, cv::Mat& dst)
{
dst = src.clone();
dst /= 255; // convert to binary image
cv::Mat prev = cv::Mat::zeros(dst.size(), CV_8UC1);
cv::Mat diff;
do {
thinningIteration(dst, 0);
thinningIteration(dst, 1);
cv::absdiff(dst, prev, diff);
dst.copyTo(prev);
} while (cv::countNonZero(diff) > 0);
dst *= 255;
}
int main()
{
RNG rng(123);
// Read image
Mat3b src = imread("path_to_image");
// Convert to grayscale
Mat1b gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// Binarize
Mat1b bin;
threshold(gray, bin, 127, 255, THRESH_BINARY_INV);
// Perform thinning
thinning(bin, bin);
// Create result image
Mat3b res = src.clone();
// Find contours
vector<vector<Point>> contours;
findContours(bin.clone(), contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
// For each contour
for (vector<Point>& contour : contours)
{
// Compute convex hull
vector<Point> hull;
convexHull(contour, hull);
// Compute circularity, used for shape classification
double area = contourArea(hull);
double perimeter = arcLength(hull, true);
double circularity = (4 * CV_PI * area) / (perimeter * perimeter);
// Shape classification
if (circularity > 0.9)
{
// CIRCLE
//{
// // Fit an ellipse ...
// RotatedRect rect = fitEllipse(contour);
// Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
// ellipse(res, rect, color, 5);
//}
{
// ... or find min enclosing circle
Point2f center;
float radius;
minEnclosingCircle(contour, center, radius);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
circle(res, center, radius, color, 5);
}
}
else if (circularity > 0.75)
{
// RECTANGLE
//{
// // Minimum oriented bounding box ...
// RotatedRect rect = minAreaRect(contour);
// Point2f pts[4];
// rect.points(pts);
// Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
// for (int i = 0; i < 4; ++i)
// {
// line(res, pts[i], pts[(i + 1) % 4], color, 5);
// }
//}
{
// ... or bounding box
Rect box = boundingRect(contour);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
rectangle(res, box, color, 5);
}
}
else if (circularity > 0.7)
{
// TRIANGLE
// Select the portion of the image containing only the wanted contour
Rect roi = boundingRect(contour);
Mat1b maskRoi(bin.rows, bin.cols, uchar(0));
rectangle(maskRoi, roi, Scalar(255), CV_FILLED);
Mat1b triangle(roi.height, roi.height, uchar(0));
bin.copyTo(triangle, maskRoi);
// Find min encolsing circle on the contour
Point2f center;
float radius;
minEnclosingCircle(contour, center, radius);
// decrease the size of the enclosing circle until it intersects the contour
// in at least 3 different points (i.e. the 3 vertices)
vector<vector<Point>> vertices;
do
{
vertices.clear();
radius--;
Mat1b maskCirc(bin.rows, bin.cols, uchar(0));
circle(maskCirc, center, radius, Scalar(255), 5);
maskCirc &= triangle;
findContours(maskCirc.clone(), vertices, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
} while (vertices.size() < 3);
// Just get the first point in each vertex blob.
// You could get the centroid for a little better accuracy
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
line(res, vertices[0][0], vertices[1][0], color, 5);
line(res, vertices[1][0], vertices[2][0], color, 5);
line(res, vertices[2][0], vertices[0][0], color, 5);
}
else
{
cout << "Some other shape..." << endl;
}
}
return 0;
}
您可以查看幾個資源。
首先,您可以考慮在answers.opencv.org上提問。 那里的opencv專家可能更集中。
其次,Samarth Brahmbhatt的Practical OpenCV一書以免費pdf格式提供,在谷歌上很容易找到。 它包含許多與您要查找的內容相關的示例。
例如,您可以分隔不同的(非重疊)輪廓,如第68頁的示例6.1所示。他有一個簡單的程序,用於查找第78頁的示例6.4中的圓和線。您還可以找到基於RANSAC的橢圓-finder(更復雜,但在這里非常有用)在第82頁的示例6.5中。
這本書是用C ++編寫的,但我認為它非常相關,只需要一個API引用就可以將它翻譯成python。
就個人而言,對於你的項目,我會一次分析一個輪廓,從他的橢圓查找器開始,並且在找不到合適的橢圓的地方,你可以使用可調閾值的霍夫變換,並截斷它們交叉處的結果線,和巴姆! 你有多邊形。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.