什么是轮廓?
- 为了获得更高的准确性,请使用二进制图像。因此,在找到轮廓之前,请应用阈值或 Canny 边缘检测。
- 从OpenCV 3.2开始,**findContours()** 不再修改源图像。
- 在OpenCV中,找到轮廓就像从黑色背景中找到白色物体。因此请记住,要找到的对象应该是白色,背景应该是黑色。
SetWorkingDir A_ScriptDir
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObject("OpenCV.cv")
img := cv.imread("12.png")
cv.imshow("Image1", img)
img_ray := cv.cvtColor(img, CV_COLOR_BGR2GRAY:=6)
;这里获取threshold()的双返回值有三种方法
; 一
; thresh_img := ComObject("OpenCV.cv.MAT")
; ret := cv.threshold(img_ray, 127, 255, CV_THRESH_BINARY:=0, thresh_img)
; contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
; 二
; ret := cv.threshold(img_ray, 127, 255, CV_THRESH_BINARY:=0)
; thresh_img := cv.extended()[1]
; contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
; 三
cv.threshold(img_ray,100, 255, CV_THRESH_BINARY:=0)
ret := cv.extended()[0]
thresh_img := cv.extended()[1]
contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
hierarchy := cv.extended()[1]
; 参数
; 在 cv.findContours() 函数中有三个参数,
; 第一个是源图像,
; 第二个是轮廓检索模式,
; 第三个是轮廓逼近方法,并输出轮廓和层次。
; 返回值
; contours:图像轮廓坐标,是一个链表
; hierarchy:[Next, Previous, First Child, Parent]
; 如果我们打印出cv.findContours()函数的返回值hierarchy,会发现它是一个包含4个值的数组:[Next, Previous, First Child, Parent]
; Next:与当前轮廓处于同一层级的下一条轮廓
; 举例来说,前面图中跟0处于同一层级的下一条轮廓是1,所以Next=1;同理,对轮廓1来说,Next=2;那么对于轮廓2呢?没有与它同一层级的下一条轮廓了,此时Next=-1。
; Previous:与当前轮廓处于同一层级的上一条轮廓
; 跟前面一样,对于轮廓1来说,Previous=0;对于轮廓2,Previous=1;对于轮廓2a,没有上一条轮廓了,所以Previous=-1。
; First Child:当前轮廓的第一条子轮廓
; 比如对于轮廓2,第一条子轮廓就是轮廓2a,所以First Child=2a;对轮廓3,First Child=3a。
; Parent:当前轮廓的父轮廓
; 比如2a的父轮廓是2,Parent=2;轮廓2没有父轮廓,所以Parent=-1。
如何绘制轮廓?
cv.drawContours(img, contours, -1, ComArrayMake([0,0,255]), 3)
ComArrayMake(inputArray)
{
arr := ComObjArray(VT_VARIANT:=12, inputArray.Length)
Loop inputArray.Length
{
arr[A_Index-1] := inputArray[A_Index]
}
return arr
}
要绘制单个轮廓,请说第四个轮廓:
cv.drawContours(img, contours, 3, ComArrayMake([0,0,255]), 3)
但是在大多数情况下,以下方法会很有用:
cnt := contours[0]
cv.drawContours(img, ComArrayMake([cnt]), 0, ComArrayMake([0,0,255]), 3)
轮廓近似法
轮廓特征
1.图像矩
SetWorkingDir A_ScriptDir
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObject("OpenCV.cv")
img := cv.imread("jiantou.png")
cv.imshow("Image1", img)
img_ray := cv.cvtColor(img, CV_COLOR_BGR2GRAY:=6)
;这里获取threshold()的双返回值有三种方法
; 一
; thresh_img := ComObject("OpenCV.cv.MAT")
; ret := cv.threshold(img_ray, 127, 255, CV_THRESH_BINARY:=0, thresh_img)
; contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
; 二
; ret := cv.threshold(img_ray, 127, 255, CV_THRESH_BINARY:=0)
; thresh_img := cv.extended()[1]
; contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
; 三
cv.threshold(img_ray, 100, 255, CV_THRESH_BINARY:=0)
ret := cv.extended()[0]
thresh_img := cv.extended()[1]
contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
hierarchy := cv.extended()[1]
cnt := contours[0]
M := cv.moments(cnt)
;M_ji 表示空间矩
;矩 m_00 给出了轮廓的区域,这等价于函数cv2.contourArea()。
m00 := M.m00
m10 := M.m10
m01 := M.m01
m20 := M.m20
m11 := M.m11
m02 := M.m02
m30 := M.m30
m21 := M.m21
m12 := M.m12
m03 := M.m03
;Mu_ji 表示中心矩
mu20 := M.mu20
mu11 := M.mu11
mu02 := M.mu02
mu30 := M.mu30
mu21 := M.mu21
mu12 := M.mu12
mu03 := M.mu03
;Nu_jl 表示归一化中心矩
nu20 := M.nu20
nu11 := M.nu11
nu02 := M.nu02
nu30 := M.nu30
nu21 := M.nu21
nu12 := M.nu12
nu03 := M.nu03
MsgBox("m00: " m00 "`n" "m10: " m10 "`n"
"m01: " m01 "`n" "m20: " m20 "`n"
"m11: " m11 "`n" "m02: " m02 "`n"
"m30: " m30 "`n" "m21: " m21 "`n"
"m12: " m12 "`n" "m03: " m03 "`n"
, "空间矩"
)
MsgBox("mu20: " mu20 "`n" "mu11: " mu11 "`n"
"mu02: " mu02 "`n" "mu30: " mu30 "`n"
"mu21: " mu21 "`n" "mu12: " mu12 "`n"
"mu03: " mu03 "`n"
, "中心矩"
)
MsgBox("nu20: " nu20 "`n" "nu11: " nu11 "`n"
"nu02: " nu02 "`n" "nu30: " nu30 "`n"
"nu21: " nu21 "`n" "nu12: " nu12 "`n"
"nu03: " nu03 "`n"
, "归一化中心矩"
)
在图像矩中,您可以提取有用的数据,例如面积,质心等。质心由关系C给出 $C_x = \frac{M_{10}}{M_{00}}$ 和 $C_y = \frac{M_{01}}{M_{00}}$。可以按照以下步骤进行:
cx := Integer(M.m10/M.m00)
cy := Integer(M.m01/M.m00)
MsgBox "质心X: " cx "`n" "质心Y: " cy
接下来在坐标处画圆
效果图:
2.轮廓面积
area := cv.contourArea(cnt)
MsgBox "轮廓面积: " area
3.轮廓周长
perimeter := cv.arcLength(cnt, ComValue(0xB, -1)) ;第二个参数给了true
MsgBox perimeter
4.轮廓近似
在数字化时,要对曲线进行采样,即在曲线上取有限个点,将其变为折线,并且能够在一定程度上保持原有的形状。
经典的Douglas-Peucker算法步骤如下:
(1)在曲线首尾两点A,B之间连接一条直线AB,该直线为曲线的弦;
(2)得到曲线上离该直线段距离最大的点C,计算其与AB的距离d;
(3)比较该距离与预先给定的阈值threshold的大小,如果小于threshold,则该直线段作为曲线的近似,该段曲线处理完毕。
(4)如果距离大于阈值,则用C将曲线分为两段AC和BC,并分别对两段取信进行1~3的处理。
(5)当所有曲线都处理完毕时,依次连接各个分割点形成的折线,即可以作为曲线的近似。
SetWorkingDir A_ScriptDir
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObject("OpenCV.cv")
img := cv.imread("jinsi.png")
cv.imshow("Image", img)
img_ray := cv.cvtColor(img, CV_COLOR_BGR2GRAY:=6)
cv.threshold(img_ray, 100, 255, CV_THRESH_BINARY:=0)
ret := cv.extended()[0]
thresh_img := cv.extended()[1]
contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
hierarchy := cv.extended()[1]
cnt := contours[0]
epsilon := 0.01*cv.arcLength(cnt, ComValue(0xB, -1))
approx := cv.approxPolyDP(cnt, epsilon, ComValue(0xB, -1))
cv.drawContours(img, ComArrayMake([approx]), 0, ComArrayMake([0,0,255]), 3)
cv.imshow("Image1", img)
cv.waitKey()
cv.destroyAllWindows()
ComArrayMake(inputArray)
{
arr := ComObjArray(VT_VARIANT:=12, inputArray.Length)
Loop inputArray.Length
{
arr[A_Index-1] := inputArray[A_Index]
}
return arr
}
下面,在第二张图片中,绿线显示了 精度 epsilon = 10% 时的近似曲线。第三幅图显示了精度 epsilon = 1% 时的情况。第三个参数指定曲线是否闭合。
5.凸包
逼近多边形是某个图像轮廓的高度近似,而凸包的提出是为了简化逼近多边形的。其实,凸包跟逼近多边形很像,只不过它是物体最外层的“凸”多边形。
简单的概括,凸包是指完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。凸包的特点是每一处都是凸的,即在凸包内连接任意两点的直线都在凸包的内部,并且任意连续3个点的内角小于180度。
凸包外观看起来与轮廓逼近相似,但并非如此(在某些情况下两者可能提供相同的结果)。在这里,**cv.convexHull()** 函数检查曲线是否存在凸凹缺陷并对其进行校正。一般而言,凸曲线是始终凸出或至少平坦的曲线。如果在内部凸出,则称为凸度缺陷。
hull := cv.convexHull(points[, hull[, clockwise[, returnPoints]]])
参数详细信息:
- points: 就是我们传入的轮廓。
- hull: 是输出,通常我们避免它。
- clockwise:方向标记。如果为True,则输出凸包为顺时针方向。否则,其方向为逆时针方向。
- returnPoints:默认情况下为True。然后返回船体点的坐标。如果为False,则返回与船体点相对应的轮廓点的索引。
因此,要获得如上图所示的凸包,以下内容就足够了:
hull := cv.convexHull(cnt)
SetWorkingDir A_ScriptDir
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObject("OpenCV.cv")
img := cv.imread("tubao.png")
cv.imshow("Image", img)
img_ray := cv.cvtColor(img, CV_COLOR_BGR2GRAY:=6)
cv.threshold(img_ray, 100, 255, CV_THRESH_BINARY:=0)
ret := cv.extended()[0]
thresh_img := cv.extended()[1]
contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
hierarchy := cv.extended()[1]
cnt := contours[0]
hull := cv.convexHull(cnt)
cv.drawContours(img, ComArrayMake([hull]), 0, ComArrayMake([0,0,255]), 2)
cv.imshow("Image1", img)
cv.waitKey()
cv.destroyAllWindows()
ComArrayMake(inputArray)
{
arr := ComObjArray(VT_VARIANT:=12, inputArray.Length)
Loop inputArray.Length
{
arr[A_Index-1] := inputArray[A_Index]
}
return arr
}
凸包缺陷
convexityDefects := cv.convexityDefects(contour, convexhull)
SetWorkingDir A_ScriptDir
cv := ComObject("OpenCV.cv")
out := ComObject("OpenCV.cv.MAT")
img := cv.imread("tubao.png")
cv.imshow("Image", img)
img_ray := cv.cvtColor(img, CV_COLOR_BGR2GRAY:=6)
cv.threshold(img_ray, 100, 255, CV_THRESH_BINARY:=0)
ret := cv.extended()[0]
thresh_img := cv.extended()[1]
contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
hierarchy := cv.extended()[1]
cnt := contours[0]
vofp := ComObject("OpenCV.VectorOfVectorOfPoint")
contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2, vofp)
vp := vofp[0]
; epsilon := 0.01*cv.arcLength(cnt, ComValue(0xB, -1))
; approx := cv.approxPolyDP(cnt, epsilon, ComValue(0xB, -1))
hull := cv.convexHull(cnt)
hull1 := cv.convexHull(cnt,,, ComValue(0xB, 0))
defects := cv.convexityDefects(cnt,hull1)
data := defects.data
loop(defects.rows)
{
r := A_Index
loop(defects.cols)
{
c := A_Index
loop(defects.channels)
{
f := numget(data, (r - 1) * (defects.cols * defects.channels * 4) + (c - 1) * defects.channels * 4 + (A_Index - 1) * 4 , "int")
a .= f "-"
if(A_Index == 3)
{
px := numget(cnt.data, f * 8, "Int")
py := numget(cnt.data, f * 8 + 4, "Int")
ax := vp[f][0]
ay := vp[f][1]
cv.circle(img,ComArrayMake([px,py]), 3, ComArrayMake([0,0,255]), -1)
}
}
a .= " | "
}
a .= "`n"
}
;cv.drawContours(img, ComArrayMake([hull]), 0, ComArrayMake([0,0,255]), 2)
cv.imshow("Image1", img)
cv.waitKey()
cv.destroyAllWindows()
ComArrayMake(inputArray)
{
arr := ComObjArray(VT_VARIANT:=12, inputArray.Length)
Loop inputArray.Length
{
arr[A_Index-1] := inputArray[A_Index]
}
return arr
}
6.检查凸度
k := cv.isContourConvex(cnt)
MsgBox(k)
7.边界矩形
7.1直边界矩形(外接矩形)
一个直矩形(就是没有旋转的矩形)。它不会考虑对象是否旋转。 所以边界矩形的面积不是最小的。可以使用函数 cv.boundingRect() 查找得到。返回值 (x, y)为矩形左上角的坐标,( w, h)是矩形的宽和高。
SetWorkingDir A_ScriptDir
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObject("OpenCV.cv")
img := cv.imread("xiefangkuai.png")
img_ray := cv.cvtColor(img, CV_COLOR_BGR2GRAY:=6)
cv.threshold(img_ray, 127, 255, CV_THRESH_BINARY:=0)
ret := cv.extended()[0]
thresh_img := cv.extended()[1]
contours := cv.findContours(thresh_img, CV_RETR_TREE:=3, CV_CHAIN_APPROX_SIMPLE:=2)
hierarchy := cv.extended()[1]
cnt := contours[0]
For k in contours{
i := cv.boundingRect(k)
img := cv.rectangle(img, i, ComArrayMake([0, 0, 255]), 2)
}
cv.imshow("Image", img)
cv.waitKey()
cv.destroyAllWindows()
ComArrayMake(inputArray)
{
arr := ComObjArray(VT_VARIANT:=12, inputArray.Length)
Loop inputArray.Length
{
arr[A_Index-1] := inputArray[A_Index]
}
return arr
}
7.2旋转的边界矩形(最小外接矩形)
这个边界矩形是面积最小的,因为它考虑了对象的旋转。用到的函数为 cv2.minAreaRect()。返回的是一个Box2D 结构,其中包含矩形左上角角点的坐标( x, y),矩形的宽和高( w, h),以及旋转角度。但是要绘制这个矩形需要矩形的 4 个角点,可以通过函数 cv.boxPoints() 获得。
s := cv.minAreaRect(cnt)
a := cv.boxPoints(s)
arr := []
loop 8{
arr.push(Integer(cv.boxPoints(s)[A_Index-1]))
}
cv.polylines(img, OpencvAHK_ConstPointConst([[arr[1], arr[2]], [arr[3],arr[4]],[arr[5],arr[6]],[arr[7],arr[8]]]), ComValue(0xB, -1), ComArrayMake([255,0,0]), 3)
8.最小外接圆
center := cv.minEnclosingCircle(cnt)
radius := cv.extended()[1]
cv.circle(img, center, radius, ComArrayMake([0,255,0]), 2)
9.拟合椭圆
ellipse := cv.fitEllipse(cnt)
cv.ellipse(img,ellipse,ComArrayMake([0,255,0]),2)
10.拟合线
图像处理中,通常会遇到根据给定的点集(比如轮廓)拟合出一条直线的情形。opencv2和opencv3中提供了一个专门用于直线拟合的函数**cv.fitLine()**。
rows := img.rows()
cols := img.cols()
line := cv.fitLine(cnt, CV_DIST_L2:=2, 0, 0.01, 0.01)
vx := line[0]
vy := line[1]
x := line[2]
y := line[3]
lefty := Integer((-x*vy/vx) + y)
righty := Integer(((cols-x)*vy/vx) + y)
cv.line(img, ComArrayMake([cols-1, righty]), ComArrayMake([0,lefty]), ComArrayMake([255,0,0]), 2)
轮廓属性
1.长宽比
aspect := cv.boundingRect(cnt)
x := aspect[0]
y := aspect[1]
w := aspect[2]
h := aspect[3]
aspect_ratio := float(w)/h
MsgBox("长宽比-直边界矩形(外接矩形):" aspect_ratio)
2.范围
area := cv.contourArea(cnt)
aspect := cv.boundingRect(cnt)
w := aspect[2]
h := aspect[3]
rect_area := w*h
extent := float(area)/rect_area
MsgBox("范围-轮廓区域与边界矩形区域的比率" extent)
3.固实性
area := cv.contourArea(cnt)
hull := cv.convexHull(cnt)
hull_area := cv.contourArea(hull)
solidity := float(area)/hull_area
MsgBox("固实性-轮廓面积与其凸包面积的比率:" solidity)
4.等效直径
area := cv.contourArea(cnt)
equi_diameter := sqrt(4*area/3.141592653589793)
MsgBox("等效直径-面积与轮廓面积相同的圆的直径:" equi_diameter)
5.方向
ellipse := cv.fitEllipse(cnt)
MsgBox(ellipse.angle())
6.遮罩和像素点
mat := ComObject("OpenCV.cv.MAT")
mask := mat.zeros(img.size(), CV_8UC1:= cv2.CV_MAKETYPE(cv2.CV_8U, 1))
cv.circle(mask,ComArrayMake([300,263]), 140, ComArrayMake([255, 255, 255]), -1)
pixelpoints := cv.findNonZero(mask)
7.最大值,最小值及其位置
cv.minMaxLoc(img_ray, mask)
min_val := cv.extended()[0]
max_val := cv.extended()[1]
min_loc := cv.extended()[2]
max_loc := cv.extended()[3]
MsgBox("最小值:" min_val "最大值:" max_val "最小位置:" "x:" min_loc[0] "y:" min_loc[1] "最大位置:" "x:" max_loc[0] "y:" max_loc[1])
8.平均颜色或平均强度
mean_val := cv.mean(img, mask)
MsgBox("B通道均值:" mean_val[0] "`n" "G通道均值:" mean_val[1] "`n" "R通道均值:" mean_val[2] "`n")
9.极端点
天黑版opencv_ahk.dll使用(改变了调用方式,优化速度…)
相关文件:https://wwz.lanzouw.com/iAkK803eaaud
cv2.ahk和log.ahk来自社区群友zzZ…
可以用文件中的天黑版的v2h版ahk运行。
示例:轮廓
#Dllload lib
#DllLoad opencv_ahk.dll
#include <cv2>
#include <log>
SetWorkingDir A_ScriptDir
;初始化opencv模块
cv := ObjFromPtr(DllCall('opencv_ahk.dll\opencv_init', 'ptr', DllCall(A_AhkPath '\ahkGetApi', 'ptr'), 'cdecl ptr'))
img := cv.imread("image/11.png")
cv.imshow("image", img)
cv.cvtColor(img, img_grey := cv.Mat(), cv2.CV_COLOR_BGR2GRAY)
cv.Canny(img_grey, canny := cv.Mat(), 80.0, 160.0)
k := cv.getStructuringElement(cv2.CV_MORPH_RECT, [3, 3], [-1, -1])
cv.dilate(canny, dilation := cv.MAT(), k)
cv.threshold(dilation, thresh_img := cv.MAT(), 127, 255, cv2.CV_THRESH_BINARY)
cv.findContours(thresh_img, contours := cv.Vector_Vector_Point(), cv2.CV_RETR_TREE, cv2.CV_CHAIN_APPROX_SIMPLE)
cv.drawContours(img, contours, 0, [0,255,0], 3)
;矩
M := cv.moments(contours[0])
m00 := M.m00
log.info('m00:' m00)
;轮廓面积
area := cv.contourArea(contours[0])
log.info('轮廓面积:' area)
;轮廓周长
perimeter := cv.arcLength(contours[0], true)
log.info('轮廓周长:' perimeter)
;轮廓近似
epsilon := 0.01*cv.arcLength(contours[0], true)
cv.approxPolyDP(contours[0], approx := cv.Vector_Point(), epsilon, true)
;cv.drawContours(img, cv.Vector_Vector_Point([approx]), 0, [0, 0, 255], 3)
;凸包
cv.convexHull(contours[0], hull := cv.Vector_Point())
;cv.drawContours(img, cv.Vector_Vector_Point([hull]), 0, [0, 0, 255], 2)
;检查凸度
k := cv.isContourConvex(contours[0])
log.info('凸度:' k)
;直边界矩形(外接矩形)
box := cv.boundingRect(contours[0])
x := box[1]
y := box[2]
w := box[3]
h := box[4]
cv.rectangle(img, [x, y], [x+w, y+h], [0,255,0], 2)
;旋转的边界矩形(最小外接矩形)
rect := cv.minAreaRect(contours[0])
;log.info(rect)
cv.boxPoints(rect, box1 := cv.Vector_Point2f())
cv.polylines(img, cv.Vector_Point(box1.toarray()), true, [0, 255, 255], 1)
;最小外接圆
cv.minEnclosingCircle(contours[0], ¢er, &radius)
;log.info(center)
;.info(radius)
cv.circle(img, center, radius, [0, 0, 255], 2)
;拟合椭圆
ellipse := cv.fitEllipse(contours[0])
cv.ellipse(img, ellipse, [0,255,0], 2)
;拟合线
cv.fitLine(contours[0], line := cv.vector_float(), cv2.CV_DIST_HUBER, 0, 0.01, 0.01)
;log.info(line[0])
vx := line[0]
vy := line[1]
x := line[2]
y := line[3]
lefty := Integer((-x*vy/vx) + y)
righty := Integer(((img.cols-x)*vy/vx) + y)
cv.line(img, [img.cols-1, righty], [0,lefty], [255,0,0], 2)
;轮廓属性
;长宽比
box := cv.boundingRect(contours[0])
x := box[1]
y := box[2]
w := box[3]
h := box[4]
aspect_ratio := float(w)/h
log.info('长宽比-(外接矩形):' aspect_ratio)
;范围
area := cv.contourArea(contours[0])
aspect := cv.boundingRect(contours[0])
w := aspect[2]
h := aspect[3]
rect_area := w*h
extent := float(area)/rect_area
log.info("范围-(轮廓区域与边界矩形区域的比率):" extent)
;固实性
area := cv.contourArea(contours[0])
cv.convexHull(contours[0], hull := cv.Vector_Point())
hull_area := cv.contourArea(hull)
solidity := float(area)/hull_area
log.info("固实性-(轮廓面积与其凸包面积的比率):" solidity)
;等效直径
area := cv.contourArea(contours[0])
equi_diameter := sqrt(4*area/3.141592653589793)
log.info("等效直径-(面积与轮廓面积相同的圆的直径):" equi_diameter)
;方向
ellipse := cv.fitEllipse(contours[0])
log.info('方向是物体指向的角度:' ellipse[5])
;遮罩和像素点
mask := cv.MAT(img.rows, img.cols, cv2.CV_8UC1, [0, 0, 0]) ;注意单通道
cv.circle(mask, [100, 60], 40, [255, 255, 255], -1)
cv.imshow("image11", mask)
cv.findNonZero(mask, pixelpoints := cv.Vector_Point())
log.info(pixelpoints.toarray())
;最大值,最小值及其位置
cv.minMaxLoc(img_grey, &minVal, &maxVal, &minLoc, &maxLoc)
log.info("最小值:" minVal "最大值:" maxVal "最小位置:" "x:" minLoc[1] "y:" minLoc[2] "最大位置:" "x:" maxLoc[1] "y:" maxLoc[2])
;平均颜色或平均强度(BGR)
mean_val := cv.mean(img, mask)
log.info(mean_val)
;极端点
log.info(contours[0].toarray())
x_array := []
y_array := []
max_right := 0
max_left := 9999
max_top := 9999
max_bot := 0
loop contours[0].Size{
x_array.push(contours[0][A_Index-1][1])
y_array.push(contours[0][A_Index-1][2])
if x_array[A_Index] > max_right{
max_right := x_array[A_Index]
max_right_y := contours[0][A_Index-1][2]
}
if x_array[A_Index] < max_left{
max_left := x_array[A_Index]
max_left_y := contours[0][A_Index-1][2]
}
if y_array[A_Index] > max_bot{
max_bot := y_array[A_Index]
max_bot_x := contours[0][A_Index-1][1]
}
if y_array[A_Index] < max_top{
max_top := y_array[A_Index]
max_top_x := contours[0][A_Index-1][1]
}
}
log.info('最右侧点:' max_right ' ' max_right_y)
log.info('最顶部点:' max_bot_x ' ' max_bot)
log.info('最左侧点:' max_left ' ' max_left_y)
log.info('最底部点:' max_top_x ' ' max_top)
cv.circle(img, [max_right, max_right_y], 5, [0, 0, 255], -1)
cv.circle(img, [max_bot_x, max_bot], 5, [0, 0, 255], -1)
cv.circle(img, [max_left, max_left_y], 5, [0, 0, 255], -1)
cv.circle(img, [max_top_x, max_top], 5, [0, 0, 255], -1)
cv.imshow("image1", img)
cv.waitKey()
cv.destroyAllWindows()
匹配形状
#Dllload lib
#DllLoad opencv_ahk.dll
#include <cv2>
#include <log>
SetWorkingDir A_ScriptDir
;初始化opencv模块
cv := ObjFromPtr(DllCall('opencv_ahk.dll\opencv_init', 'ptr', DllCall(A_AhkPath '\ahkGetApi', 'ptr'), 'cdecl ptr'))
img1 := cv.imread("image/111.png")
img2 := cv.imread("image/123.png")
cv.imshow("image", img2)
cv.cvtColor(img1, img_grey1 := cv.Mat(), cv2.CV_COLOR_BGR2GRAY)
cv.threshold(img_grey1, thresh_img1 := cv.MAT(), 127, 255, cv2.CV_THRESH_BINARY)
cv.findContours(thresh_img1, contours1 := cv.Vector_Vector_Point(), cv2.CV_RETR_TREE, cv2.CV_CHAIN_APPROX_SIMPLE)
cv.drawContours(img1, contours1, 0, [0, 0, 255], 3)
cv.cvtColor(img2, img_grey2 := cv.Mat(), cv2.CV_COLOR_BGR2GRAY)
cv.threshold(img_grey2, thresh_img2 := cv.MAT(), 127, 255, cv2.CV_THRESH_BINARY)
cv.findContours(thresh_img2, contours2 := cv.Vector_Vector_Point(), cv2.CV_RETR_TREE, cv2.CV_CHAIN_APPROX_SIMPLE)
cnt1 := contours1[0]
loop 3{
M := cv.moments(contours2[A_Index-1])
cx := Integer(M.m10/M.m00)
cy := Integer(M.m01/M.m00)
cv.circle(img2, [cx, cy], 3, [0, 0, 255], -1)
cnt2 := contours2[A_Index-1]
cv.drawContours(img2, contours2, A_Index-1, [0, 0, 255], 3)
ret := cv.matchShapes(cnt1, cnt2, 1, 0.0)
cv.putText(img2, ret, [cx, cy], 0, 0.5, [255, 0, 255], 1)
log.info(ret)
}
cv.imshow("image1", img1)
cv.imshow("image2", img2)
cv.waitKey()
cv.destroyAllWindows()
匹配自身结果=0.0
匹配旋转后的自身结果=0.008218551978777078
匹配矩形结果=0.3271618191421985
有错误请联系我改正!
天黑主项目地址:https://github.com/thqby/opencv_ahk
本系列所有贡献者(AutoHotKey中文社区群友)不分先后:天黑请闭眼,zzZ…,演好自己,僵尸,城西,Tebayaki。
学习了
学习学习
多谢老朱