凸包特征检测
凸包就是图像的最小外接多边形,通过图像的轮廓点,找到距离最远的两个点的直线,根据直线找到距离最远的下一个点,直到所有的点被包围在多边形内
- 读取图像
- 二值化
- 找图像的轮廓
- 获取凸包点的坐标
- 绘制凸包点
convexHull
获得图像的凸包点cv2.convexHull(points, hull=None, clockwise=False, returnPoints=True)
- points 输入图像的轮廓
polylines
绘制图像的凸包点cv2.polylines(image, pts, isClosed, color, thickness=1)
- pts 二维数组,每个数组是一个多边形的每个坐标点
- isClosed 是否闭合,True会连接起点和终点
num = cv2.imread('./media/num310.png')
num_gray = cv2.imread('./media/num310.png',cv2.IMREAD_GRAYSCALE) # 灰度
_,num_erzhi = cv2.threshold(num_gray,127,255,cv2.THRESH_BINARY) # 二值化
contours,h = cv2.findContours(num_erzhi,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 这里的轮廓有三套
# cv2.drawContours(num,contours,-1,(0,255,0),2) # 绿色的轮廓
hull = [cv2.convexHull(contours[i]) for i in range(3)] # 每一套轮廓一个一套凸包点 生成凸包点的列表
cv2.polylines(num,hull,True,(0,0,255),2 # 红色的凸包
轮廓特征查找
轮廓特征查找,就是绘制图像的外接矩形,最小外接矩形,最小外接圆
- 外接矩形
通过遍历边缘点,所有边缘点的坐标最大值和最小值
cv2.boundingRect()
返回左上角的坐标x,y和矩形的宽高w,h
num = cv2.imread('./media/num310.png')
num_gray = cv2.imread('./media/num310.png',cv2.IMREAD_GRAYSCALE)
_,num_erzhi = cv2.threshold(num_gray,127,255,cv2.THRESH_BINARY)
contours,h = cv2.findContours(num_erzhi,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# cv2.drawContours(num,contours,-1,(0,255,0),2)
# 外接矩形
for cont in contours:
x, y, w, h = cv2.boundingRect(cont) # 获得左上角的点和 矩形的宽和高
cv2.rectangle(num,(x,y),(x+w,y+h),(127,127,0),2)
cv2.imshow('num',num)
- 最小外接矩形
使用旋转卡壳法
以凸包的一条边为起始边,遍历凸包点,找到距离最远的凸包点,做起始边的平行线,找凸包点投影距离最远的点,作矩形剩下的两边,就得到了最小外接矩形
minAreaRect
传入轮廓,计算出最小外接矩形,返回 坐标,宽度,高度,旋转角度
boxPoints
把最小外接矩形得到的数组,转化为四个顶点坐标 4行二列 四个点
num = cv2.imread('./media/num310.png')
num_gray = cv2.imread('./media/num310.png',cv2.IMREAD_GRAYSCALE)
_,num_erzhi = cv2.threshold(num_gray,127,255,cv2.THRESH_BINARY)
contours,h = cv2.findContours(num_erzhi,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# cv2.drawContours(num,contours,-1,(0,255,0),2)
# 最小外接矩形
for cont in contours:
rect = cv2.minAreaRect(cont)
# 计算最小外接矩形 ((109.34503936767578, 167.05064392089844), (147.49758911132812, 67.03535461425781), 74.38900756835938)
rect = cv2.boxPoints(rect).astype(np.int32) # 返回坐标,转化为整数
cv2.drawContours(num,[rect],-1,(127,0,127),2) # 连接点 也可以使用绘制先线遍历绘制
cv2.imshow('num',num)
- 最小外接圆
cv2.minEnclosingCircle
计算最小外接圆,返回圆心(x,y)和半径
num = cv2.imread('./media/num310.png')
num_gray = cv2.imread('./media/num310.png',cv2.IMREAD_GRAYSCALE)
_,num_erzhi = cv2.threshold(num_gray,127,255,cv2.THRESH_BINARY)
contours,h = cv2.findContours(num_erzhi,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# cv2.drawContours(num,contours,-1,(0,255,0),2)
for cont in contours:
(x,y),r = cv2.minEnclosingCircle(cont)
cv2.circle(num,(int(x),int(y)),int(r),(200,200,0),2) # 需要转化为整数
直方图均衡化
直方图,不同像素值的数量统计
- 绘制直方图
nut = cv2.imread('./media/nut.png')
hist = cv2.calcHist([nut],[0],mask=None,histSize=[256],ranges=[0,256])
# 每种像素值统计数量
min_v,max_v,minloc,maxloc = cv2.minMaxLoc(hist)
# 获得最大值 最小值 最小值的坐标 最大值的坐标
bar = np.zeros((256,256,3),dtype=np.uint8)
# 初始化图像 得到全黑的图像
h = (max_v-min_v)*1.2
for i,value in enumerate(hist):
higit = int((value/h)*256) # 归一化
cv2.line(bar,(i,256),(i,256-higit),(127,0,127)) # 绘制直线
cv2.imshow('bar',bar)
- 直方图均衡化
通过把直方图拉平,让像素值分布个均匀,可以让提高图像的对比度。凸显图像的更多细节,较暗的区域更清晰,但是较亮的区域会更亮
通过统计每个像素点的比例,重新映射到0-255的范围
nut = cv2.imread('./media/nut.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow('nut',nut)
cv2.imshow('nut2',cv2.equalizeHist(nut))
- 对比度受限的直方图均衡化
对小范围进行均衡化,然后合并图像,但是如果有斑点噪声点,会放大噪声点。
直方图均衡化过程中引入一个对比度限制参数。如果有过大的极端值,会进行平滑处理
通过对图像进行重采样,对小区域的图像分别进行直方图均衡化,进行插值,让小区域的图像合并更平滑
clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)
clipLimit 对比度限制参数,设置一个大于1的值,会限制对比度增强的最大程度,避免过度放大噪声。
tileGridSize 图像的分块大小
创建这个类之后使用apply处理图像
clta = cv2.createCLAHE()
nut3 = clta.apply(nut)
cv2.imshow('nut3',nut3)
模板匹配
模板(小图)在一个较大的图中滑动比较,通过不同的比较方法判断是否匹配成功
res=cv2.matchTemplate(image, templ, method)
image 图像
templ模板
method 方法
返回匹配计算的结果,结果矩阵和原图相同,值的大小代表匹配的相似度
method:
- cv2.TM_CCOEFF 相关系数匹配 计算模板和目标区域的两个均值,模板和目标的像素分别和其均值的差在相乘,再求和
- cv2.TM_CCOEFF_NORMED 对相关系数匹配归一化 越接近1 匹配度越高
- cv2.TM_CCORR 相关匹配 对应像素的乘积 越大匹配度越高
- cv2.TM_CCORR_NORMED 相关匹配归一化 越大匹配度越高
- cv2.TM_SQDIFF 平方差匹配 平方差公式 计算 对应像素的差的平方和
- cv2.TM_SQDIFF_NORMED 归一化平方差 越小匹配度越高
bg = cv2.imread('./media/game.png')
templ = cv2.imread('./media/temp.png')
bg_gray = cv2.cvtColor(bg,cv2.COLOR_RGB2GRAY)
templ_gray = cv2.cvtColor(templ,cv2.COLOR_RGB2GRAY)
result = cv2.matchTemplate(bg_gray,templ_gray,cv2.TM_SQDIFF_NORMED)
h,w = templ_gray.shape
y,x = np.where(result<0.2) # 返回索引
for x,y in zip(x,y):
cv2.rectangle(bg,(x,y),(x+w,y+h),(0,0,255),2)
cv2.imshow('bg',bg)
cv2.imshow('templ',templ)
霍夫变换
把图像中的像素点映射到霍夫空间,转换坐标系的表示。
每个像素点坐标,在霍夫空间中是一条直线,霍夫空间中的点在原图像的坐标空间是一条线。
在霍夫空间中,多条线交与一个点,在原图中就是多个点共线。
这里使用极坐标系进行表示
lines=cv2.HoughLines(image, rho, theta, threshold)
rho
:r的精度,以像素为单位,表示霍夫空间中每一步的距离增量, 值越大,考虑越多的线。theta
:角度θ的精度,通常以弧度为单位,表示霍夫空间中每一步的角度增量。值越小,考虑越多的线。- threshold 阈值,直线上点的数量超过阈值才认为是一条直线
- 返回二维数组,在霍夫空间的rho和theta
- 霍夫直线变换
num = cv2.imread('./media/num310.png')
num_gray = cv2.cvtColor(num,cv2.COLOR_BGR2GRAY)
_,erzhi = cv2.threshold(num_gray,127,255,cv2.THRESH_BINARY)
boder = cv2.Canny(erzhi,20,70)
lines = cv2.HoughLines(boder,0.8,0.01,50) # 边缘检测
for line in lines:
ro,theta = line[0] # 得到极坐标的两个值
a = np.cos(theta)
b = np.sin(theta)
x1 = 0
x2 = 1000 #设置得足够大 让直线得以显示
y1 = (ro-a*x1)/b # 计算两个点
y2 = (ro-a*x2)/b
cv2.line(num,(int(x1),int(y1)),(int(x2),int(y2)),(0,0,255),1) # 绘制直线
cv2.imshow('num_gray',boder)
cv2.imshow('num',num)
- 统计概率霍夫直线变换
通过得到的直线的两个端点,统计连线上像素点的数量,通过设定的阈值筛选不符合条件的点
image
:输入图像,白点表示边缘点=rho
:极径分辨率,表示极坐标系中的距离theta
:极角分辨率,以弧度为单位threshold
:阈值,过滤掉弱检测结果lines
(可选):一个可初始化的输出数组,用于存储检测到的直线参数。minLineLength
(可选):最短长度阈值maxLineGap
(可选):同一直线两点之间的最大距离- 如果
maxLineGap
设置得较小,那么只有相邻且间距很小的线段才会被连接起来,这可能导致检测到的直线数量较多,但更准确地反映了图像中的局部直线结构。 - 如果
maxLineGap
设置得较大,则线段间的间距可以更大,这样可能会合并更多的局部线段成为更长的直线,但有可能会将原本不属于同一直线的线段误连接起来。
- 如果
lines = cv2.HoughLinesP(boder,0.8,0.01,50,minLineLength=60,maxLineGap=400)
# print(lines.shape) # (2, 1, 4)
for line in lines:
x1,y1,x2,y2 = line[0]
cv2.line(num,(x1,y1),(x2,y2),(0,0,255),1)
cv2.imshow('num_gray',boder)
cv2.imshow('num',num)
- 霍夫圆变换
circles=cv2.HoughCircles(image, method, dp, minDist, param1, param2)
-
image
:输入图像,灰度图像 -
method
:使用的霍夫变换方法cv2.HOUGH_GRADIENT
-
dp
:设置为1表示霍夫梯度法中累加器图像的分辨率与原图一致 -
minDist
:检测到的圆心之间的最小允许距离,以像素为单位。 -
param1
和param2
:这两个参数是在使用cv2.HOUGH_GRADIENT
方法时的特定参数-
param1
(可选):阈值1,决定边缘强度的阈值。 -
param2
:阈值2,控制圆心识别的精确度。
-
cirs = cv2.HoughCircles(num_gray,cv2.HOUGH_GRADIENT,1,400,param2=50)
for c in cirs[0,:]:
# print(c)
cv2.circle(num,(int(c[0]),int(c[1])),int(c[2]),(0,255,0))
cv2.imshow('num_gray',num_gray)
cv2.imshow('num',num)
亮度变换 窗口管理 滑动条
通过对图像中的数据进行操作达到亮度透明度的变换
所有像素加一个值,亮度增大,减一个值,亮度减小
线性变换
cv2.addWeighted(src1, alpha, src2, beta, gamma)
alpha 第一个图的权重
beta 第二个图的权重
gamma 偏置,亮度补偿 整体加上这个值
直接像素值修改
numpy.clip(a, a_min, a_max)
小于a_min 修改为a_min
大于a_max修改为a_max
nut = cv2.imread('./media/nut.png')
h,w,_ = nut.shape
fir1 = cv2.imread('./media/1.jpg')
fir2 = fir1[100:h+100,100:w+100]
img = cv2.addWeighted(fir2,1,nut,0.5,10)
cv2.imshow('nut',nut)
cv2.imshow('fir1',fir1)
cv2.imshow('img',img)
窗口管理 滑动条
创建窗口cv2.namedWindow(str)
创建滑块
bar = cv2.createTrackbar(trackbar_name,‘img_bar’,init_v,max_v,thres)
trackbar_name 滑块名称
要显示的窗口名
初始值
最大值,最小值是0
函数 拖动滑块,把滑块的值传入这个函数,并调用这个函数
cv2.namedWindow('img_bar')
def thres(x):
fir1 = cv2.imread('./media/1.jpg')
fir1 = cv2.cvtColor(fir1,cv2.COLOR_BGR2GRAY)
_,erzhi = cv2.threshold(fir1,x,255,cv2.THRESH_BINARY)
cv2.imshow('img_bar',erzhi)
trackbar_name = 'threshold'
max_v = 255
init_v = 127
bar = cv2.createTrackbar(trackbar_name,'img_bar',init_v,max_v,thres)