Files
digital-image-processing/8/2/description.py
T
2024-05-23 10:50:05 +08:00

339 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#coding='utf-8'
import cv2
import numpy as np
import random
import math
def draw_contours(imgColor,minArea=50):
if len(imgColor.shape)>2:
imgResult=cv2.cvtColor(imgColor,cv2.COLOR_BGR2GRAY)
else:
imgResult = imgColor.copy()
ret, img_binary = cv2.threshold(imgResult, 125, 255, 0)
contours0, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours=list([])
for cnt in contours0:
rect = cv2.minAreaRect(cnt)
if rect[1][0] * rect[1][1] < minArea: # 此矩形区域的面积小于10,则忽略
continue
contours.append(cnt)#contours=contours.append(cnt)会返回NoneType错误
cv2.drawContours(imgResult, contours, -1, (0, 0, 255), 3)
return imgResult
def getDis(pointX, pointY, lineX1, lineY1, lineX2, lineY2):
'''返回点线之间的距离
'''
a = lineY2 - lineY1
b = lineX1 - lineX2
c = lineX2 * lineY1 - lineX1 * lineY2
dis = (math.fabs(a * pointX + b * pointY + c)) / (math.pow(a * a + b * b, 0.5))
return dis
def get_region_description(imgColor,minArea=50):
if len(imgColor.shape)>2:
imgResult=cv2.cvtColor(imgColor,cv2.COLOR_BGR2GRAY)
else:
imgResult = imgColor.copy()
_, imgBinary = cv2.threshold(imgResult, 125, 255, 0)
row,col=imgBinary.shape[:2]
contours0, hierarchy = cv2.findContours(imgBinary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 去除小面积区域
contours_=[]
for cnt in contours0:
rect = cv2.minAreaRect(cnt)
if rect[1][0] * rect[1][1] < minArea: # 此矩形区域的面积小于10,则忽略
continue
contours_.append(cnt)#contours=contours.append(cnt)会返回NoneType错误
cv2.drawContours(imgResult, contours_, -1, (0, 0, 255), 3)
contours=[] #存放区域描述子,包括重心、周长、面积、圆形度、矩形度
# 进行区域描述
for cnt in contours_:
oneContour = [] # 存放一个区域的描述子
# minAreaRect返回值:(最小外接矩形的中心(x,y),(宽度,高度),旋转角度)
#角度是【-90,0】,0度会写成-90度,方向不准确
rect = cv2.minAreaRect(cnt)
# maxRegion=min(rect[1][0],rect[1][1])/2
# maxV=maxRegion*maxRegion #区域最大半径平方值
points=cv2.boxPoints(rect)#可以返回四个点的值
#获得重心
cx,cy=int(rect[0][0]),int(rect[0][1])
oneContour.append((cx,cy)) #添加重心
# #获得区域方向,并绘制方向
'''minV = math.inf #区域上的点到直线距离和的最小值
min_i = 0
min_j = 0
for i in range(row):
for j in range(col):
sum = 0
# 获得一个区域最大半径圆上的点,做点到质心的线
if (i - cx) * (i - cx) + (j - cy) * (j - cy) == maxV: # 一个圆上的点
for c0 in cnt[:][:]: # cnt中存放中区域轮廓上的点坐标
sum += getDis(c0[0][0], c0[0][1], cx, cy, i, j)
if sum < minV:
minV = sum
min_i = i
min_j = j
cv2.line(imgResult, (cx, cy), (min_i, min_j), (0, 0, 0), 3, 8)
regionDirect = math.atan2(min_j-cy,min_i-cx)
# regionDirect = rect[2]
oneContour.append(regionDirect)'''
minV = math.inf
findPoint=(0,0)
rectPoints=[]#获得区域最上边、下边、左边、右边线上的点,做点到中心的线
minRow=int(min(points[i][0] for i in range(4))) #最小行坐标
maxRow=int(max(points[i][0] for i in range(4)))
minCol=int(min(points[i][1] for i in range(4)))
maxCol=int(max(points[i][1] for i in range(4)))
highPointNum=int(maxRow-minRow+1)
widthPointNum=int(maxCol-minCol+1)
fixI1=zip([minRow]*widthPointNum,range(minCol,maxCol+1,1))
rectPoints.extend(fixI1)
fixI2=zip([maxRow]*widthPointNum,range(minCol,maxCol+1,1))
rectPoints.extend(fixI2)
fixJ1=zip(range(minRow,maxRow+1,1),[minCol]*highPointNum)
rectPoints.extend(fixJ1)
fixJ2=zip(range(minRow,maxRow+1,1),[maxCol]*highPointNum)
rectPoints.extend(fixJ2)
for point in rectPoints:
sum = 0
for c0 in cnt[:][:]: # cnt中存放中区域轮廓上的点坐标
sum += getDis(c0[0][0], c0[0][1], cx, cy, point[0],point[1])
if sum < minV:
minV = sum
findPoint=point
cv2.line(imgResult, (cx, cy), findPoint, (0, 0, 0), 3, 8)
regionDirect =math.atan2(cy-findPoint[1],cx-findPoint[0])*180/3.14
oneContour.append(regionDirect) # 添加重心
# 计算轮廓面积和周长
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt,False)
circle = 4.0*np.pi*area/(perimeter*perimeter) # 计算圆形度
rectangle = area/(rect[1][0]*rect[1][1]) # 计算矩形度
oneContour.append(area)
oneContour.append(perimeter)
oneContour.append(circle)
oneContour.append(rectangle)
contours.append(oneContour)
return imgResult,contours
# Hu Moments 胡不变矩 wiki: https://en.wikipedia.org/wiki/Image_moment
# 公式 https://docs.opencv.org/4.1.2/d3/dc0/group__imgproc__shape.html#gab001db45c1f1af6cbdbe64df04c4e944
# 代码 https://github.com/opencv/opencv/blob/b6a58818bb6b30a1f9d982b3f3f53228ea5a13c1/modules/imgproc/src/moments.cpp
# void cv::HuMoments( const Moments& m, double hu[7] )
# {
# CV_INSTRUMENT_REGION();
#
# double t0 = m.nu30 + m.nu12;
# double t1 = m.nu21 + m.nu03;
#
# double q0 = t0 * t0, q1 = t1 * t1;
#
# double n4 = 4 * m.nu11;
# double s = m.nu20 + m.nu02;
# double d = m.nu20 - m.nu02;
#
# hu[0] = s;
# hu[1] = d * d + n4 * m.nu11;
# hu[3] = q0 + q1;
# hu[5] = d * (q0 - q1) + n4 * t0 * t1;
#
# t0 *= q0 - 3 * q1;
# t1 *= 3 * q0 - q1;
#
# q0 = m.nu30 - 3 * m.nu12;
# q1 = 3 * m.nu21 - m.nu03;
#
# hu[2] = q0 * q0 + q1 * q1;
# hu[4] = q0 * t0 + q1 * t1;
# hu[6] = q1 * t0 - q0 * t1;
# }
#求图像的24个矩
def get_moments(img,moments=None):
# row == heigh == Point.y
# col == width == Point.x
# Mat::at(Point(x, y)) == Mat::at(y,x)
# https://blog.csdn.net/puqian13/article/details/87937483
mom = [0] * 10 #10个原点矩
umom = [0] * 7 #7个中心矩
numom = [0] * 7 #7个归一化中心矩
rows, cols = img.shape
for y in range(rows):
# temp var
x0 = 0
x1 = 0
x2 = 0
x3 = 0
for x in range(cols):
p = img[y,x]
xp = x * p
xxp = x * xp
x0 = x0 + p
x1 = x1 + xp
x2 = x2 + xxp
x3 = x3 + xxp * x
py = y * x0
sy = y*y
mom[9] += (py) * sy # m03
mom[8] += (x1) * sy # m12
mom[7] += (x2) * y # m21
mom[6] += x3 # m30
mom[5] += x0 * sy # m02
mom[4] += x1 * y # m11
mom[3] += x2 # m20
mom[2] += py # m01
mom[1] += x1 # m10
mom[0] += x0 # m00
x_a = mom[1] / mom[0]
y_a = mom[2] / mom[0]
for y in range(0,rows):
x_a_0 = 0
x_a_1 = 0
x_a_2 = 0
x_a_3 = 0
for x in range(0,cols):
p = img[y,x]
x_a_0 = x_a_0 + p
x_a_1 += p * (x - x_a )
x_a_2 += x_a_1 * (x - x_a )
x_a_3 += x_a_2 * (x - x_a )
y_a_1 = (y - y_a)
y_a_2 = y_a_1 * (y - y_a)
y_a_3 = y_a_2 * (y - y_a)
umom[0] += x_a_2
umom[1] += x_a_1 * y_a_1
umom[2] += x_a_0 * y_a_2
umom[3] += x_a_3
umom[4] += x_a_2 * y_a_1
umom[5] += x_a_1 * y_a_2
umom[6] += x_a_0 * y_a_3
cx = mom[1] * 1.0 / mom[0]
cy = mom[2] * 1.0 / mom[0]
umom[0] = mom[3] - mom[1] * cx
umom[1] = mom[4] - mom[1] * cy
umom[2] = mom[5] - mom[2] * cy
umom[3] = mom[6] - cx * (3 * umom[0] + cx * mom[1])
umom[4] = mom[7] - cx * (2 * umom[1] + cx * mom[2]) - cy * umom[0]
umom[5] = mom[8] - cy * (2 * umom[1] + cy * mom[1]) - cx * umom[2]
umom[6] = mom[9] - cy * (3 * umom[2] + cy * mom[2])
# nu
inv_sqrt_m00 = np.sqrt(abs(1.0 / mom[0]))
s2 = (1.0 / mom[0]) * (1.0 / mom[0])
s3 = s2 * inv_sqrt_m00
numom[0] = umom[0] * s2
numom[1] = umom[1] * s2
numom[2] = umom[2] * s2
numom[3] = umom[3] * s3
numom[4] = umom[4] * s3
numom[5] = umom[5] * s3
numom[6] = umom[6] * s3
moments = mom + umom + numom
return moments
def get_hu_moments(numom):
'''
:param numom: 图像的原点矩
:return: 返回7个不变矩
'''
hu = [0]*7
t0 = numom[3] + numom[5]
t1 = numom[4] + numom[6]
q0 = t0 * t0
q1 = t1 * t1
n4 = 4 * numom[1];
s = numom[0] + numom[2];
d = numom[0] - numom[2];
hu[0] = s
hu[1] = d * d + n4 * numom[1]
hu[3] = q0 + q1
hu[5] = d * (q0 - q1) + n4 * t0 * t1
t0 *= q0 - 3 * q1
t1 *= 3 * q0 - q1
q0 = numom[3] - 3 * numom[5]
q1 = 3 * numom[4] - numom[6] ;
hu[2] = q0 * q0 + q1 * q1
hu[4] = q0 * t0 + q1 * t1
hu[6] = q1 * t0 - q0 * t1
return hu
def moment_invariants(img):
textLists=[] #用来存放几何变换名称
imgLists=[] #用来存放几何变换图像
# 旋转45度
rotate_45_matrix = cv2.getRotationMatrix2D((img.shape[1] // 2, img.shape[0] // 2), -45, 1)
rotate_45_image = cv2.warpAffine(img, rotate_45_matrix, dsize=(img.shape[1], img.shape[0]))
textLists.append("rotate-45")
imgLists.append(rotate_45_image)
# 平移
M = np.float32([[1, 0, 50], [0, 1, 50]])
translation_img = cv2.warpAffine(img, M, dsize=(img.shape[1], img.shape[0]))
textLists.append("translation")
imgLists.append(translation_img)
# 顺时针旋转90
rotate_90_iamge = cv2.rotate(img, rotateCode=cv2.ROTATE_90_CLOCKWISE)
textLists.append("rotate-90")
imgLists.append(rotate_90_iamge)
# 顺时针旋转180
rotate_180_iamge = cv2.rotate(img, cv2.ROTATE_180)
textLists.append("rotate-180")
imgLists.append(rotate_180_iamge)
# 缩小一半
resize_0_5_img=np.zeros(img.shape)
halfImg = cv2.resize(img, (img.shape[1] // 2, img.shape[0] // 2))
textLists.append("shrink ")
imgLists.append(halfImg)
# 逆时针旋转90
rotate_270_image = cv2.rotate(img, rotateCode=cv2.ROTATE_90_COUNTERCLOCKWISE)
textLists.append("rotate-270")
imgLists.append(rotate_270_image)
# calc the moments of the image
'''get 24 moments
10 spatial moments 空间矩、原点矩
m00 m10 m01 m20 m11 m02 m30 m21 m12 m03
7 central moments 七个中心矩
mu20 mu11 mu02 mu30 mu21 mu12 mu03
7 central normalized moments个归一化中心矩
nu20 nu11 nu02 nu30 nu21 nu12 nu03
本来应该都是10个的 but
mu00 = m00, nu00 = 1 nu10 = mu10 = mu01 = mu10 = 0, hence the values are not stored.
原理 见论文
论文 https://pdfs.semanticscholar.org/afc2/e9d5dfbd666bf4dd34adeb78a17393c8ee64.pdf?_ga=2.259665167.462545856.1577780532-1866022657.1577780532
refrence https://docs.opencv.org/4.1.2/d8/d23/classcv_1_1Moments.html#a8b1b4917d1123abc3a3c16b007a7319b
https://github.com/opencv/opencv/blob/b6a58818bb6b30a1f9d982b3f3f53228ea5a13c1/modules/imgproc/src/moments.cpp'''
huMomentLists=[]
for img in imgLists:
m_=cv2.moments(img)
hu = cv2.HuMoments(m_)
huMomentLists.append(np.array(hu))
# normal hu matrix
huMomentLists = np.abs(huMomentLists)
huMomentLists = np.log(huMomentLists)
huMomentLists = np.abs(huMomentLists)
return imgLists,textLists,huMomentLists
if __name__=='__main__':
img=cv2.imread(r'.\img\describe.png')
# img=draw_contours(img)
# cv2.imshow("contours",img)
imgReturn,contours=get_region_description(img)
i=0
for cnt in contours:
i+=1
print("第%d个区域信息:"%i)
print("质心:",cnt[0])
print("方向:%f度"%cnt[1])
print("面积:", cnt[2])
print("周长:", cnt[3])
print("圆形度:", cnt[4])
print("矩形度:", cnt[5])
cv2.imshow("result",imgReturn)
cv2.waitKey(0)