OpenCV处理图像

OpenCV

1.学前准备

pip install OpenCV-python

测试能否正常使用:

import cv2
print(cv2.__version__)

2.图片基本处理

2.1图片读取

cv.imread(文件地址)

示例代码:

import cv2 as cv
import numpy as np
path = "./imgs/cat1.png"
img=cv.imread(path,cv.IMREAD_COLOR)
cv.imshow("cat_pic",img)
h,w,c=img.shape
cv.waitKey(0)
cv.destroyAllWindows()

2.2numpy数组转化为图片

创建一个三维的数组

大小为:height*weight*3(颜色通道),数据类型转化为uint8

img=np.zeros((height,width,3),np.uint8)#参数:宽,高,通道

import cv2 
import numpy as np
height,width = 480,640
img=np.zeros((height,width,3),np.uint8)#参数:宽,高,通道
print(img.shape)
img[:,:,:]=np.random.randint(125,255,img.shape)#random.randint参数:随机数范围,图片尺寸
print(img.shape)#打印图片的尺寸
#打印图片维度
print(img.ndim)
cv2.imshow("img",img)#显示图片
cv2.waitKey(0)#等待键盘输入
cv2.destroyAllWindows()#销毁所有窗口

2.3在图片里绘制图形

2.3.1绘制直线

cv.line(参数:图片,起始点,终止点,颜色,线宽)

import cv2 as cv
cat=cv.imread('./imgs/cat1.png')
cv.line(cat,(0,0),(cat.shape[1],cat.shape[0]),(0,255,0),2)#参数:图片,起始点,终止点,颜色,线宽
2.3.2绘制圆形

cv.cirle(参数:图片,圆心,半径,颜色,线宽 线宽为-1则填充为实心圆)

cv.circle(cat,(cat.shape[1]//2,cat.shape[0]//2),100,(0,255,0),-1)#参数:图片,圆心,半径,颜色,线宽 线宽为-1则填充为实心圆
2.3.3绘制矩形

cv.rectangle(参数:图片,左上角点,右下角点,颜色,线宽,-1,填充为实心)

cv.rectangle(cat,(cat.shape[1],cat.shape[0]),(cat.shape[1]//4*3,cat.shape[0]//4*3),(0,255,0),-1)
2.3.4绘制文字(仅英文)

cv.putText(参数:图片,字,坐标,字体,字体大小,颜色,线宽)

cv.putText(cat,'Hello',(cat.shape[1]//2,cat.shape[0]//2),cv.FONT_HERSHEY_SIMPLEX,1,(0,0,255),2)

线的类型:#cv.LINE_AA为抗锯齿: cv.LINE_AA, cv.LINE_4, cv.LINE_8, cv.LINE_AA

2.4图像保存

cv.imwrite("存储位置",需要存储的图像)

3.视频处理

3.1读取视频

import cv2 as cv
cap=cv.VideoCapture('./img/7233x3100 (4).mp4')
while True:
    ret,frame=cap.read()#参数为True时,读取下一帧,为False时,读取最后一帧
    if ret==False:
        print('读取失败')
        break
    cv.imshow('frame',frame)
    if cv.waitKey(40)&0xFF==ord('q'):#意思:回第八位的值,0xFF为十六进制的FF,即二进制的11111111
        break
cap.release()  
cv.destroyAllWindows()  

4.图像预处理

4.1图像预处理准备

读取图片

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img=cv.imread('./imgs/cat1.png')
cv.resize(img,(480,480))
cv.imshow('cat',img)
cv.waitKey(0)
cv.destroyAllWindows()

4.2图像翻转

cv2.flip(img,flipcode)
参数:

  • img: 要翻转的图像
  • flipcode: 指定翻转类型的标志
    • flipcode=0: 垂直翻转,图片像素点沿x轴翻转
    • flipcode>0: 水平翻转,图片像素点沿y轴翻转
    • flipcode<0: 沿原点翻转,与原图中心对称

示例代码:

cat_fx=cv.flip(img,0)
cat_fy=cv.flip(img,1)
cat_fxy=cv.flip(img,-1)
cv.imshow('cat',img)
cv.imshow('cat_fx',cat_fx)#沿x轴翻转上下
cv.imshow('cat_fy',cat_fy)#沿y轴翻转左右
cv.imshow('cat_fxy',cat_fxy)#沿着原点中心翻转
cv.waitKey(0)
cv.destroyAllWindows()

4.3图像仿射变换

使用关键仿射变换函数

cv.warpAffline(参数:图片名,仿射变换矩阵,图片大小,插值方法)

得到仿射变换矩阵M --> 带入仿射变换函数 --> 输出

4.3.1图像旋转

设置参数旋转中心,旋转角度,缩放比例

使用cv.getRotationMatrix2D(设置旋转中心,旋转角度,缩放比例)

示例代码:

h,w,_=img.shape
#设置旋转中心
center=(w//2,h//2)
angle=0
scale=0.01#缩放比例
M=cv.getRotationMatrix2D(center,angle,scale)#仿射变换矩阵
#使用仿射变换函数进行变换旋转
img_rotate=cv.warpAffine(img,M,(w,h),flags=cv.INTER_LINEAR)#参数:图片名,仿射变换矩阵,输出图片大小,插值方法
cv.imshow('img_rotate',img_rotate)
cv.imshow('cat',img)
cv.waitKey(0)
cv.destroyAllWindows()
4.3.2图像平移

平移:读取图像-》设置参数(平移量tx,ty)-》获取平移矩阵np.float32-》仿射变换函数进行平移-》显示图像

tx = 200
ty = 100
M = np.float32([[1,0,tx],[0,1,ty]])
print(M)

img_remove = cv.warpAffine(img,M,(w,h))#挪移后多余的像素点舍弃
cv.imshow('img_remove',img_remove)
cv.waitKey(0)
cv.destroyAllWindows()
4.3.3图像缩放

M仿射矩阵:设置缩放比例

M = np.float32([[缩放比例, 0, 0], [0, 缩放比例, 0]])

#读取图像
M = np.float32([[0.1, 0, 0], [0, 0.1, 0]])
img_little = cv.warpAffine(img, M, (img.shape[1], img.shape[0]))
cv.imshow('img',img_little)
cv.waitKey(0)
cv.destroyAllWindows()

4.3.4图像剪切

示例代码

import numpy as np 
import cv2 as cv
#读取图像
shx=0.2
shy=0.2
img=cv.imread('./imgs/cat1.png')
img=cv.resize(img,(300,300))
M_y = np.float32([[1,shy, 0], [0, 1, 0]])
M_x = np.float32([[1, 0, 0], [shx, 1, 0]])
M_xy= np.float32([[1,shy, 0], [shx, 1, 0]])
M= np.float32([[1,0, 0], [0, 1, 0]])
img_little_y = cv.warpAffine(img, M_y, (360, 360))
img_little_x = cv.warpAffine(img, M_x, (360, 360))
img_little_xy = cv.warpAffine(img, M_xy, (360, 360))
img_li=cv.warpAffine(img, M, (360, 360))
cv.imshow('img_y',img_little_y)
cv.imshow('img_x',img_little_x)
cv.imshow('img_xy',img_little_xy)
cv.imshow('img_li',img_li)
cv.waitKey(0)
cv.destroyAllWindows()

4.4图片压缩

4.4.1最近邻插值法

new_img=cv.warpAffine(img,M,(300,300),flags=cv.INTER_NEAREST)
cv.imshow("new_img",new_img)
cv.waitKey(0)
cv.destroyAllWindows()

4.4.2双线性插值法

new_img=cv.warpAffine(img,M,(300,300),flags=cv.INTER_LINEAR)
cv.imshow("new_img",new_img)
cv.waitKey(0)
cv.destroyAllWindows()

4.4.3像素区域插值法

st=cv.imread('./img/blackstocking.png')
st=cv.resize(st,(160,240))
dst_x=cv.warpAffine(st,M,(160,240),flags=cv.INTER_NEAREST)
dst_l=cv.warpAffine(st,M,(160,240),flags=cv.INTER_LINEAR)
dst_c=cv.warpAffine(st,M,(160,240),flags=cv.INTER_AREA)#像素区域插值
cv.imshow('st',st)
cv.imshow('dst_x',dst_x)
cv.imshow('dst_l',dst_l)
cv.imshow('dst_c',dst_c)
cv.waitKey(0)
cv.destroyAllWindows()
4.3.4其他方法
4.4.4.1双三次插值法
cv.warpAffine(img,M,(cols,rows),flags=cv.INTER_CUBIC)
4.4.4.2Lanczos插值
cv.warpAffine(img,M,(cols,rows),flags=cv.INTER_LANCZOS4)

4.5图像边缘填充

4.5.1边缘填充准备

旋转缩放图像做准备

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
face=cv.imread("./images/face.png")
#获取旋转矩阵
scale=0.1
angle=45
center=(face.shape[1]//2,face.shape[0]//2)
M=cv.getRotationMatrix2D(center,angle,scale)
#使用仿射变换矩阵
img=cv.warpAffine(face,M,(face.shape[1],face.shape[0]))
cv.imshow("img",img)    
cv.imshow("face",face)
cv.waitKey(0)
cv.destroyAllWindows()
4.5.2边缘复制

cv.warpAffine(参数:图像,输出图片大小,插值方式,边界模式

borderMode=cv.BORDER_REPLICATE

img_boradercv=cv.warpAffine(face,M,(face.shape[1],face.shape[0]),cv.INTER_LANCZOS4,borderMode=cv.BORDER_REPLICATE)
cv.imshow("img",img)    
cv.imshow("face",face)
cv.imshow("img_boradercv",img_boradercv)
cv.waitKey(0)
cv.destroyAllWindows()
4.5.3边界反射

borderMode=cv.BORDER_REFLECT

img_reflect = cv.warpAffine(face,M,(face.shape[1],face.shape[0]), cv.INTER_LANCZOS4,borderMode=cv.BORDER_REFLECT)
cv.imshow('reflect', img_reflect)
cv.imshow("img",img)    
cv.imshow("face",face)
cv.imshow("img_boradercv",img_boradercv)
cv.waitKey(0)
cv.destroyAllWindows()
4.5.4边界反射_101

borderMode=cv.BORDER_REFLECT101

img_reflect101=cv.warpAffine(face,M,dsize=(img.shape[1],img.shape[0]),flags=cv.INTER_LINEAR,borderMode=cv.BORDER_REFLECT101)
cv.imshow('reflect', img_reflect)
cv.imshow("img",img)    
cv.imshow("face",face)
cv.imshow("img_boradercv",img_boradercv)
cv.imshow("img_reflect101",img_reflect101)
cv.waitKey(0)
cv.destroyAllWindows()
4.5.5边缘常数填充

borderMode=cv.BORDER_CONSTANT,borderValue=(0,0,255)

img_ch=cv.warpAffine(face,M,(face.shape[1],face.shape[0]),cv.INTER_LANCZOS4,borderMode=cv.BORDER_CONSTANT,borderValue=(0,0,255))
cv.imshow('img_ch',img_ch)
cv.waitKey(0)
cv.destroyAllWindows()
4.5.6边缘包裹

borderMode=cv.BORDER_WRAP

img_wrap=cv.warpAffine(face,M,(face.shape[1],face.shape[0]),cv.INTER_LANCZOS4,borderMode=cv.BORDER_WRAP)
cv.imshow('wrap',img_wrap)
cv.waitKey(0)
cv.destroyAllWindows()

4.6图像矫正(透视变换)

步骤:对原图需要矫正位置的坐标进行框中选定,输出图像的大小坐标分别创建两个numpy数组

使用cv.getPerspectiveTransform(pic1,pic_2)创建仿射变换矩阵M

使用cv.warpPerspective(img_tou,M,(w,h),flags=cv.INTER_LINEAR,borderMode=cv.BORDER_CONSTANT)

cv.warpPerspective(参数:图像片,变换矩阵,输出尺寸,插值方法,边界填充方式)

import numpy as np
pic1=np.float32([[182,113],[521,141],[115,294],[501,340]])
print(pic1[0].shape)

M = cv.getPerspectiveTransform(pic1,pic_2)
new_img=new_pic = cv.warpPerspective(img_tou,M,(w,h),flags=cv.INTER_LINEAR,borderMode=cv.BORDER_CONSTANT)#参数:图像片,变换矩阵,输出尺寸,插值方法,边界填充方式
cv.imshow('new_img',new_img)
cv.imshow('img_tou',img_tou)
cv.waitKey(0)
cv.destroyAllWindows()

4.7图像叠加

4.7.1直接相加
pig=cv.imread('./images/pig.png')
cao=cv.imread('./images/cao.png')
img_numpy=cao+pig
print(img_numpy)
4.7.2add函数饱和运算
img_add=cv.add(pig,cao)
print(cv.add(pig,cao))#add函数
4.7.3加权求和运算
img_weight=cv.addWeighted(pig,0.5,cao,0.5,0)#加权求和运算·

4.8图像处理转换

函数cv.cvtcolor(参数:转换方式)

4.8.1rgb转换为gray

cat_grey=cv.cvtColor(cat,cv.COLOR_RGB2GRAY)

import cv2 as cv
cat=cv.imread('./images/cat1.png')
cv.imshow('cat',cat)
cat_grey=cv.cvtColor(cat,cv.COLOR_RGB2GRAY)
cv.imshow('cat_grey',cat_grey)
cv.waitKey(0)
cv.destroyAllWindows()
4.8.2rgb转换为hsv

cat_hsv=cv.cvtColor(cat,cv.COLOR_BGR2HSV)

cat_hsv=cv.cvtColor(cat,cv.COLOR_BGR2HSV)
cv.imshow("cat_hsv",cat_hsv)
cv.waitKey(0)
cv.destroyAllWindows()

4.9灰度实验

4.9.1最大值灰度转换

使用for循环迭代,取颜色通道中的最大值,并且赋值到颜色通道上去

import cv2 as cv
import numpy as np
pig_y=cv.imread('./images/pig.png')
pig=cv.imread('./images/pig.png')
for i in range(pig.shape[0]):
    for j in range(pig.shape[1]):
        pig[i][j][0]=max(pig[i][j])
        pig[i][j][1]=max(pig[i][j])
        pig[i][j][2]=max(pig[i][j])
cv.imshow('pig',pig)
cv.imshow('pig_y',pig_y)
cv.waitKey(0)
cv.destroyAllWindows()
4.9.2平均值灰度
import cv2 as cv
import numpy as np
pig = cv.imread("./images/pig.png")
h,w,_ = pig.shape
av_grey=np.zeros((h,w),dtype=np.uint8)#uint8:0~255
for i in range(h):
    for j in range(w):
        av_grey[i,j] = int(sum((pig[i,j])))//3
cv.imshow("av",av_grey)
cv.imshow("pig",pig)
cv.waitKey(0)
cv.destroyAllWindows()
4.9.3加权灰度
wr,wg,wb=0.299,0.576,0.114
import cv2 as cv
import numpy as np
pig = cv.imread("./images/pig.png")
h,w,_ = pig.shape
weight_grey=np.zeros((h,w),dtype=np.uint8)#uint8:0~255
for i in range(h):
    for j in range(w):
        weight_grey[i,j] = np.array([pig[i,j,0]*wr,pig[i,j,1]*wg,pig[i,j,2]*wb]).sum()/3
cv.imshow("av",weight_grey)
cv.imshow("pig",pig)
cv.waitKey(0)
cv.destroyAllWindows()

4.10图像二值化处理

cv.threshold(图片,阈值,maxval,方法)

4.10.1阈值法(cv.THRESH_BINARY)

小于等于阈值的像素就被设置为0(通常代表背景),大于阈值的像素就被设置为maxval(通常代表前景)

4.10.2反阈值法(THRESH_BINARY_INV)

像素值大于阈值时,该像素值将会变成0(黑),当灰度图的像素值小于等于阈值时,该像素值将会变成maxval

4.10.3截断阈值法(THRESH_TRUNC)

像素值大于阈值的部分将会被修改为阈值,小于等于阈值的部分不变。

4.10.4低阈值零处理(THRESH_TOZERO)

像素值小于等于阈值的部分被置为0(也就是黑色),大于阈值的部分不变。

4.10.5超阈值零处理(THRESH_TOZERO_INV)

像素值大于阈值的部分置为0(也就是黑色),像素值小于等于阈值的部分不变。

4.10.6OSTU阈值法(THRESH_OTSU)

采取最大类间距的策略,该方法是使用是寻找最佳阈值

4.10.7自适应二值法

cv.adaptiveThreshold(参数1:灰度图,参数2:最大值,参数3:自适应阈值方法,参数4:二值化方法,参数5:窗口大小,参数6:偏移量)

明暗分布不均匀可以使用自适应二值化

4.10.7.1均值法(ADAPTIVE_THRESH_MEAN_C)

示例代码:

img_mean=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,5,10)
cv.imshow('mean',img_mean)
cv.waitKey(0)
cv.destroyAllWindows()
4.10.7.2加权求和法(ADAPTIVE_THRESH_GAUSSIAN_C)

示例代码:

img_guss=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,11,2)
cv.imshow('img_guss',img_guss)
cv.waitKey(0)
cv.destroyAllWindows()

4.11制作图像掩膜

4.11.1制作掩膜及其修改颜色步骤
  • 图像掩膜是图像处理中的一个重要概念,它用于对图像进行遮罩,从而实现对图像的裁剪、过滤、增强等操作。

  • 设置:color_low=np.array([23,46,46]) color_high=np.array([34,255,255])

  • 根据hsv表创建掩膜,最大值和最小值,掩膜输出的是目标区域显示为白色,其他为黑色

  • 参数:src:源图像,lowerb:颜色下限,upperb:颜色上限,dst:输出图像

  • 做与运算输出,输出结果将目标白色替换为原色,其他不变

颜色bgr转hsv

import cv2 as cv
import numpy as np
demo=cv.imread("./images/demo.png")
demo=cv.resize(demo,(480,480))
#颜色bgr转hsv
demo_hsv=cv.cvtColor(demo,cv.COLOR_BGR2HSV)

创建掩膜:cv.inRange(src,lowerb,upperb,dst)

#创建掩膜:cv.inRange(src,lowerb,upperb),参数:src:源图像,lowerb:颜色下限,upperb:颜色上限
color_low=np.array([23,46,46])
color_high=np.array([34,255,255])
mask=cv.inRange(demo_hsv,color_low,color_high)#只显示黑白色

做与运算输出掩膜cv.bitwise_and(demo,demo,mask=mask)

#创建掩膜与原图大小一样,是一个二值化图像
mask_and=cv.bitwise_and(demo,demo,mask=mask)#做两次运算前两个颜色,再与掩膜运算

将原图目标色进行替换

#修改颜色
demo[mask==255]=[0,255,0]
cv.imshow('demo',demo)
cv.waitKey(0)
cv.destroyAllWindows()
4.11.2掩膜实例–添加水印
4.11.2.1ROI切割

也就是使用numpy数组进行切割,不作赘述

4.11.2.2添加水印实例

步骤:

从原图切下需要添加水印的区域,并进行灰度处理

import cv2 as cv
import numpy as np
logo=cv.imread('images/logohq.png')
bg=cv.imread('images/bg.png')
h,w=logo.shape[:2]
logo_grey=cv.cvtColor(logo,cv.COLOR_BGR2GRAY)
bg_zi=bg[:h,:w]

分别将logo图进行二值化,分别使用阈值法和反阈值法进行变换

阈值法白底黑字

_,logo_grey_black=cv.threshold(logo_grey,170,255,cv.THRESH_BINARY)

反阈值法白字黑底

_,logo_grey_binary=cv.threshold(logo_grey,170,255,cv.THRESH_BINARY_INV)

分别将两个所得图原logo图和截取区域进行与运算

将白字替换为原来的颜色,制作掩膜

logo_and=cv.bitwise_and(logo,logo,mask=logo_grey_binary)#掩膜

将另一张图的黑字与截取区域作与运算,得到一个黑字logo和原背景图结合的图片

logo_and_black=cv.bitwise_and(bg_zi,bg_zi,mask=logo_grey_black)

对所得掩膜进行add运算

bg_zi[:]=cv.add(logo_and,logo_and_black)

4.12噪点消除

4.12.1均值滤波

cv.blur(img,(3,3))

  • 参数:目标图,核尺寸(只能为单数)
import cv2 as cv
#均值滤波cv.blur
img=cv.imread("./images/lvbo2.png")
img_blur=cv.blur(img,(3,3))#均值滤波
cv.imshow("img",img)
cv.imshow("img_blur",img_blur)
cv.waitKey(0)
cv.destroyAllWindows()
4.12.2方框滤波

cv.boxFilter(img, -1, (3,3),normalize=True、False)

  • 参数:输入图像,输出图像的深度,核大小,是否归一化
#方框滤波cv.boxFilter
img_box=cv.boxFilter(img, -1, (3,3),normalize=True)#参数:输入图像,输出图像的深度,核大小,是否归一化
img_box_F=cv.boxFilter(img, -1, (3,3),normalize=False)
cv.imshow('img_box',img_box)
cv.imshow('img_box_F',img_box_F)
cv.waitKey(0)
cv.destroyAllWindows()
4.12.3高斯滤波

cv.GaussianBlur(img,(3,3),1)

  • 参数:1、原图;2、核大小;3、标准差
#高斯滤波:好用cv.GaussianBlur
img_gauss=cv.GaussianBlur(img,(3,3),1)#参数:1、原图;2、核大小;3、标准差
cv.imshow('img_gauss',img_gauss)
cv.imshow("img",img)
cv.imshow("img_blur",img_blur)    
cv.waitKey(0)
cv.destroyAllWindows()
4.12.4中值滤波

cv.medianBlur(img_jy, 5)

  • 更适合椒盐噪声
  • 参数1.原图,2.核大小
img_jy=cv.imread("images/lvbo3.png")#更适合椒盐噪声
#中值滤波:cv.medianBlur
img_median = cv.medianBlur(img_jy, 5)#参数1.原图,2.核大小
cv.imshow("median", img_median)
cv.imshow("jy", img_jy)
cv.waitKey(0)   
cv.destroyAllWindows()
4.12.5双边滤波

cv.bilateralFilter(img,9,75,75)

  • 参数:图片,区域大小,高斯核,高斯核
img_doubleb=cv.bilateralFilter(img,9,75,75)#参数:图片,区域大小,高斯核,高斯核
cv.imshow('img_doubleb',img_doubleb)
cv.waitKey(0)
cv.destroyAllWindows()

4.13图像梯度处理

滤波是应用卷积来实现的,卷积的关键就是卷积核,我们来考察下面这个卷积核:
k1=[−101−202−101] k1=\left[\begin{array}{c c c}{{-1}}&{{0}}&{{1}}\\ {{-2}}&{{0}}&{{2}}\\ {{-1}}&{{0}}&{{1}}\end{array}\right] k1= 121000121

4.13.1sobel算子

cv.Sobel(img,-1,1,1,3),会进行边缘反射_101,再进行计算

参数:图像,深度,横向,纵向,核大小

横向可以设置1或者0,为1则绘制横向边缘

纵向同理

import cv2 as cv
img_sobel=cv.Sobel(img,-1,1,1,3)
img_sobel_x=cv.Sobel(img,-1,1,0,3)#求纵向边缘
img_sobel_y=cv.Sobel(img,-1,0,1,3)#求横向边缘
cv.imshow("sobel",img_sobel)
cv.imshow("sobel_x",img_sobel_x)
cv.imshow("sobel_y",img_sobel_y)
cv.waitKey(0)
cv.destroyAllWindows()
4.13.2Laplacian算子

cv.Laplacian(img,-1)

参数:输入图像,输出图像的深度,核大小,偏移量,核类型(以上示例只填写了前两个)

import numpy as np
# 模拟一张图像,灰度图
img=np.array([[100,102,109,110,98,20,19,18,21,22],
             [109,101,98,108,102,20,21,19,20,21],
             [109,102,105,108,98,20,22,19,19,18],
             [109,98,102,108,102,20,23,19,20,22],
             [109,102,105,108,98,20,22,19,20,18],
             [100,102,108,110,98,20,19,18,21,22],
             [109,101,98,108,102,20,22,19,20,21],
             [109,102,108,108,98,20,22,19,19,18],
              ],dtype=np.float32)
img_la=cv.Laplacian(img,-1)#参数:输入图像,输出图像的深度,核大小,偏移量,核类型
print(img)
cv.imshow('laplacian',img_la)
print(img_la)

输出:

[[ 22.   3. -28. -17. -58.  77.   4.   6.  -4.  -4.]
 [-25.   7.  31. -14. -84.  83.  -4.   2.   0.  -4.]
 [-14.   5. -10. -13. -60.  80.  -5.   3.   1.   9.]
 [-22.  23.   8. -12. -84.  85.  -9.   5.   0. -12.]
 [-23.   6.   0. -11. -64.  80.  -7.   3.  -2.  12.]
 [ 22.   3. -17. -18. -62.  77.   6.   6.  -4.  -7.]
 [-25.   7.  33. -14. -84.  84.  -8.   3.   0.  -4.]
 [-14.  11. -26. -10. -60.  80.  -5.   3.   1.   8.]]

4.14边缘检测

不是一个算子,是完整的一整套方案。

4.14.1 高斯滤波

就是去除噪点!

边缘检测本身属于锐化操作,对噪点比较敏感,所以需要进行平滑处理。这里使用的是一个5*5的高斯核对图像进行消除噪声。上一个实验中已经介绍了高斯滤波的具体过程,这里就不再过多叙述,只展示一下用到的5*5的高斯核:

4.14.2 计算图像的梯度与方向

这里使用了sobel算子来计算图像的梯度值,在上一章节中,我们了解到sobel算子其实就是一个核值固定的卷积核,如下所示:
sobel(水平方向)=[−101−202−101] sobel(水平方向)=\left[\begin{array}{c c c}{{-1}}&{{0}}&{{1}}\\ {{-2}}&{{0}}&{{2}}\\ {{-1}}&{{0}}&{{1}}\end{array}\right] sobel(水平方向)= 121000121

sobel(垂直方向)=[−1−2−1000121] sobel(垂直方向)=\left[\begin{array}{c c c}{{-1}}&{{-2}}&{{-1}}\\ {{0}}&{{0}}&{{0}}\\ {{1}}&{{2}}&{{1}}\end{array}\right] sobel(垂直方向)= 101202101

首先使用sobel算子计算中心像素点的两个方向上的梯度GxG_{x}GxGyG_{y}Gy,然后就能够得到其具体的梯度值:
G=Gx2+Gy2 G={\sqrt{G_{x}{}^{2}+G_{y}{}^{2}}} G=Gx2+Gy2
也可以使用G=∣Gx+Gy∣G=|G_{x}+G_{y}|G=Gx+Gy来代替。在OpenCV中,默认使用G=∣Gx+Gy∣G=|G_{x}+G_{y}|G=Gx+Gy来计算梯度值。

然后我们根据如下公式可以得到一个角度值

GyGx=tan⁡ (θ){\frac{G_{\mathrm{y}}}{G_{x}}}=\tan\,(\theta)GxGy=tan(θ)
θ=arctan⁡ (GyGx) \theta=\arctan\,({\frac{G_{\mathrm{y}}}{G_{x}}}) θ=arctan(GxGy)
这个角度值其实是当前边缘的梯度的方向。通过这个公式我们就可以计算出图片中所有的像素点的梯度值与梯度方向,然后根据梯度方向获取边缘的方向。

a). 并且如果梯度方向不是0°、45°、90°、135°这种特定角度,那么就要用到插值算法来计算当前像素点在其方向上进行插值的结果了,然后进行比较并判断是否保留该像素点。这里使用的是单线性插值,通过A1和A2两个像素点获得dTmp1与dTmp2处的插值,然后与中心点C进行比较(非极大值抑制)。具体的插值算法请参考图像旋转实验。

b). 得到θ\thetaθ的值之后,就可以对边缘方向进行分类,为了简化计算过程,一般将其归为四个方向:水平方向、垂直方向、45°方向、135°方向。并且:

θ\thetaθ值为-22.5°~22.5°,或-157.5°~157.5°,则认为边缘为水平边缘;

当法线方向为22.5°~67.5°,或-112.5°~-157.5°,则认为边缘为45°边缘;

当法线方向为67.5°~112.5°,或-67.5°~-112.5°,则认为边缘为垂直边缘;

当法线方向为112.5°~157.5°,或-22.5°~-67.5°,则认为边缘为135°边缘;

4.14.3 非极大值抑制

得到每个边缘的方向之后,其实把它们连起来边缘检测就算完了,但是为什么还有这一步与下一步呢?是因为经过第二步得到的边缘不经过处理是没办法使用的,因为高斯滤波的原因,边缘会变得模糊,导致经过第二步后得到的边缘像素点非常多,因此我们需要对其进行一些过滤操作,而非极大值抑制就是一个很好的方法,它会对得到的边缘像素进行一个排除,使边缘尽可能细一点。

在该步骤中,我们需要检查每个像素点的梯度方向上的相邻像素,并保留梯度值最大的像素,将其他像素抑制为零。假设当前像素点为(x,y),其梯度方向是0°,梯度值为G(x,y),那么我们就需要比较G(x,y)与两个相邻像素的梯度值:G(x-1,y)和G(x+1,y)。如果G(x,y)是三个值里面最大的,就保留该像素值,否则将其抑制为零。

4.14.4代码用法

cv2.Canny(image, threshold1, threshold2),

参数:图像,阈值1,阈值2

即使读到的是彩色图也可以进行处理。

#边缘检测
img=cv.imread('images/lvbo2.png')
ret,img_binary=cv.threshold(img,127,255,cv.THRESH_BINARY)
img_ga=cv.GaussianBlur(img,(3,3),1)#作用:高斯模糊,作用:降噪
ret,img_ga_binary=cv.threshold(img_ga,127,255,cv.THRESH_BINARY)
dst_img=cv.Canny(img_binary,30,70)#参数:二值化后的图像,阈值1,阈值2
dst_img_gau=cv.Canny(img_ga_binary,30,70)
cv.imshow('dst_img_gau',dst_img_gau)
cv.imshow('dst_img',dst_img)
cv.waitKey(0)
cv.destroyAllWindows()

4.15绘制图像轮廓

4.15.1轮廓的定义

- 轮廓是一系列连续的点和组成的曲线,代表了物体的基本外形。是一个闭合和封闭的图形

4.15.2操作步骤

1、先对图像进行预处理

转灰度,二值化

#绘制轮廓步骤,RETR_EXTERNAL 只绘制最外轮廓 存储中:CHAIN_APPROX_SIMPLE
num_y=cv.imread("../images/num.png")
#读图转灰度
number=cv.imread("../images/num.png",cv.IMREAD_GRAYSCALE)
#二值化
_,num_t=cv.threshold(number,127,255,cv.THRESH_OTSU+cv.THRESH_BINARY_INV)

2、关键操作:调用cv.findContours(目标图,找轮廓点的种类,保存方法)会返回两个值:轮廓坐标集合和层级结构

  • 常用的种类
  1. RETR_EXTERNAL

表示只查找最外层的轮廓。并且在hierarchy里的轮廓关系中,每一个轮廓只有前一条轮廓与后一条轮廓的索引,而没有父轮廓与子轮廓的索引。

2.3.4.会查找所有轮廓,但会有层级关系。

  1. RETR_LIST

表示列出所有的轮廓。并且在hierarchy里的轮廓关系中,每一个轮廓只有前一条轮廓与后一条轮廓的索引,而没有父轮廓与子轮廓的索引。

  1. RETR_CCOMP

表示列出所有的轮廓。并且在hierarchy里的轮廓关系中,轮廓会按照成对的方式显示。

RETR_CCOMP 模式下,轮廓被分为两个层级:

  • 层级 0:所有外部轮廓(最外层的边界)。
  • 层级 1:所有内部轮廓(孔洞或嵌套的区域)。
  1. RETR_TREE

表示列出所有的轮廓。并且在hierarchy里的轮廓关系中,轮廓会按照树的方式显示,其中最外层的轮廓作为树根,其子轮廓是一个个的树枝。

#查找轮廓
contours,h=cv.findContours(num_t,mode=cv.RETR_EXTERNAL,method=cv.CHAIN_APPROX_SIMPLE)

绘制轮廓

使用cv.drawContours()将要绘制在什么图上,深度,所得的轮廓坐标列表,颜色,绘制线宽度依次传入

num=cv.drawContours(num_y,contours,-1,(0,0,255),2)

显示图像

cv.imshow("num_t",num_t)  
cv.waitKey(0)
cv.destroyAllWindows()

4.16绘制凸包

4.16.1定义和方法

在进行凸包特征检测之前,首先要了解什么是凸包。通俗的讲,凸包其实就是将一张图片中物体的最外层的点连接起来构成的凸多边形,它能包含物体中所有的内容。

一般来说,凸包都是伴随着某类点集存在的,也被称为某个点集的凸包。

对于一个点集来说,如果该点集存在凸包,那么这个点集里面的所有点要么在凸包上,要么在凸包内。

凸包检测常用在物体识别、手势识别、边界检测等领域。穷举法时间复杂度高不特殊情况不采用

  • 穷举法
  • QuickHull法
4.16.2quickhull法的代码实现步骤

预处理灰度和二值化

#读图
img=cv.imread('../images/tu.png')

#转灰度
img_g=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#二值化
ret,img_b=cv.threshold(img_g,127,255,cv.THRESH_BINARY)

查找轮廓

#查找轮廓
ct,h=cv.findContours(img_b,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)

将凸包找出

#寻找凸包
hull=cv.convexHull(ct[0])
print(hull)

绘制凸包

#绘制凸包
cv.polylines(img,[hull],True,(0,255,0),1,cv.LINE_AA)
#显示图像
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

4.17绘制矩形

4.17.1绘制正向的一般矩形

旋转卡壳法

读图、图像预处理、灰度、二值化

白色图像为目标

#绘制矩形
import  cv2 as cv
img=cv.imread('../images/num.png')
num=img.copy()
num_gray=cv.cvtColor(num,cv.COLOR_BGR2GRAY)
_,num_binary=cv.threshold(num_gray,150,255,cv.THRESH_BINARY_INV)#反阈值法

找到轮廓,并且绘制

c,h=cv.findContours(num_binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
img_Con=cv.drawContours(img,c,-1,(0,0,255),2)

使用cv.boundingRect(cnt),找出矩形坐标,返回x,y和矩形的长和宽是

利用cv.rectangle绘制出来

for cnt in c:
    x,y,w,h=cv.boundingRect(cnt)
    print(cnt,end=',')
    cv.rectangle(img_Con,(x,y),(x+w,y+h),(0,255,0),1,cv.LINE_AA)
cv.imshow('drawing img_Con',img_Con)
cv.waitKey(0)
cv.destroyAllWindows()
4.17.2绘制最小矩形

无关方向,最小能包裹目标图像的矩形

读图、图像预处理、灰度、二值化

白色图像为目标

#绘制矩形
import  cv2 as cv
img=cv.imread('../images/num.png')
num=img.copy()
num_gray=cv.cvtColor(num,cv.COLOR_BGR2GRAY)
_,num_binary=cv.threshold(num_gray,150,255,cv.THRESH_BINARY_INV)#反阈值法

找到轮廓,并且绘制

c,h=cv.findContours(num_binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
img_Con=cv.drawContours(img,c,-1,(0,0,255),2)

使用cv.minAreaRect的方式已找到,cv.minAreaRect(),返回最小外接矩形的中心点、宽度和角度

利用cv.boxPoints(rect)将中心、尺寸和角度转换为四个角点坐标

并且转化为int类型

使用cv.polylines()绘制参数:目标图,坐标列表,是否闭合,颜色,宽度,线形

for cnt in c:
        # 计算最小外接矩形
    rect = cv.minAreaRect(cnt)#返回最小外接矩形的中心点、宽高和角度

    # 将最小外接矩形的中心、尺寸和角度转换为四个角点坐标
    box = cv.boxPoints(rect)
    box = box.astype('int')  # 转换为整数类型
    
    # 绘制旋转后的最小外接矩形
    cv.polylines(img, [box], isClosed=True, color=(0, 0, 255), thickness=1, lineType=cv.LINE_AA)
    
    #绘制矩阵
    x,y,w,h=cv.boundingRect(cnt)
    cv.rectangle(img_Con,(x,y),(x+w,y+h),(255,0,0),2,cv.LINE_AA)
    
    #绘制凸包
    hull = cv.convexHull(cnt)
    # cv.drawContours(img_Con, [hull], 0, (0, 255, 0), 2, cv.LINE_AA)
    cv.polylines(img_Con, [hull], isClosed=True, color=(0, 255, 0), thickness=2, lineType=cv.LINE_AA)
4.17.3最小外接圆

4.18直方图和直方图均衡化

4.18.1绘制直方图的步骤

读图

利用函数cv.calcHist([img],[2],None,[256],[0,256])

返回一个数组列表,代表每一个像素值的像素数量

参数:图片,通道,掩膜,直方图大小,直方图范围

  • 图片:目标图片
  • 通道:指定通道
  • 掩膜:可以对指定的白色区域进行绘制直方图,也可以为None
  • 直方图大小:可以设定将像素值分为多少组进行统计
  • 直方图范围:一般为[0,256]

再使用cv.minMaxLoc提取最少数量像素值及其索引像素值和最多数量像素值及其索引像素值

import matplotlib.pyplot as plt
img=cv.imread('../images/lvbo.png')
hist=cv.calcHist([img],[2],None,[256],[0,256])#计算直方图,参数:图片,通道,掩膜,直方图大小,直方图范围
minVal,maxVal,minLoc,maxLoc=cv.minMaxLoc(hist) #返回值最少的像素个数,最多的像素个数,最小值位置索引,最大值位置索引

可以使用matplotlib进行直线绘制

# 绘制直方图
plt.figure(figsize=(6, 4))
plt.plot(hist, color='r')

使用cv.line进行绘制直方图,将数据归一化,先限制直方图高度

arr=np.zeros((256,256,3),np.uint8)
#限制直方图的高
hpt=int(0.9*256)
#使用cv.line 画出直方图
for i in range(256):
    cv.line(arr,(i,256),(i,256-int(hist[i].item()*hpt/maxVal)),(0,0,255),2,1)
cv.imshow('hist',arr)
cv.waitKey(0)
cv.destroyAllWindows()

将绘制直方图的代码封装成函数

def draw(img,m,color):
    hist=cv.calcHist([img],[m],None,[256],[0,256])
    minVal,maxVal,minLoc,maxLoc=cv.minMaxLoc(hist) #返回值最少的像素个数,最多的像素个数,最小值位置索引,最大值位置索引
    arr=np.zeros((256,256,3),np.uint8)
    #限制直方图的高
    hpt=int(0.9*256)
    #使用cv.line 画出直方图
    for i in range(256):
        cv.line(arr,(i,256),(i,256-int(hist[i].item()*hpt/maxVal)),color,2,1)
    return arr
img=cv.imread('../images/lvbo.png')
cv.imshow("hist",draw(img,2,(0,0,255)))
cv.waitKey(0)
cv.destroyAllWindows()
4.18.2直方图均衡化

同样的进行灰度化

#读图
img=cv.imread('../images/zhifang.png')
#转灰度图
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)

直方图均衡化使用cv.equalizeHist(gray)

eqh=cv.equalizeHist(gray)#直方图均衡化
dst_eqh=draw(eqh,0,(0,255,0))
cv.imshow('eqh',eqh)
cv.imshow('dst_eqh',dst_eqh)

对比度受限自适应直方图变化:

clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)

  • clipLimit(可选):对比度限制参数,用于控制直方图均衡化过程中对比度增强的程度。如果设置一个大于1的值(如2.0或4.0),CLAHE会限制对比度增强的最大程度,避免过度放大噪声。如果不设置,OpenCV会使用一个默认值。
  • tileGridSize(可选):图像分块的大小,通常是一个包含两个整数的元组,如(8, 8),表示将图像划分成8x8的小块进行独立的直方图均衡化处理。分块大小的选择会影响到CLAHE的效果以及处理速度。

创建CLAHE对象后,可以使用 .apply() 方法对图像进行CLAHE处理:

img=clahe.apply(image)

  • image:要均衡化的图像。
  • img均衡后的图像
clahe=cv.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))#对比度受限自适应直方图变化
img_clahe=clahe.apply(gray)#意义:对比度受限
cv.imshow('img_clahe',img_clahe)
dst_clahe=draw(img_clahe,0,(0,255,0))
cv.imshow('dst_clahe',dst_clahe)

图像输出:

dst_gray=draw(gray,0,(0,0,255))
cv.imshow('img',img)
cv.imshow('gray',gray)
cv.imshow('dst_gray',dst_gray)
cv.waitKey(0)
cv.destroyAllWindows()

4.19模板匹配

4.19.1模版匹配步骤

进行匹配,读取原图和目标图并且进行转灰度图

import cv2 as cv
import numpy as np
#读图
img=cv.imread('../images/game.png')#目标图
temp=cv.imread('../images/temp.png')#模板图
#转灰度
img_gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)#目标图转灰度
temp_gray=cv.cvtColor(temp,cv.COLOR_BGR2GRAY)

使用cv.matchTemplate(原图,目标图,匹配的方法)

匹配方法,可以是以下之一:

  • cv2.TM_CCOEFF
  • cv2.TM_CCOEFF_NORMED
  • cv2.TM_CCORR
  • cv2.TM_CCORR_NORMED
  • cv2.TM_SQDIFF
  • cv2.TM_SQDIFF_NORMED
  • 这些方法决定了如何度量模板图像与原图像子窗口之间的相似度。

返回值res

函数在完成图像模板匹配后返回一个结果矩阵,这个矩阵的大小与原图像相同。矩阵的每个元素表示原图像中相应位置与模板图像匹配的相似度。

设置阈值,使用np.where()的函数获取符合条件的坐标

threshold=0.8
loc=np.where(res>=threshold)
h,w=temp.shape[:2]#获取目标图的长和宽

使用zip(loc)返回一个迭代器*(解包操作)**

进行图像绘制

for pt in zip(*loc):#py===>(y,x)
    leftupper=pt[::-1]#(x,y)
    rightbottom=(pt[1]+w,pt[0]+h)#(x+w,y+h)
    cv.rectangle(img,leftupper,rightbottom,(0,0,255),2)
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

4.20霍夫变换

4.20.1霍夫变换的基本理论

霍夫变换是图像处理的一种技术,主要用于检测图像中的直线、圆等几何形状。基本思想就是将图像空间中的点映射到参数空间中,通过在参数空间中寻找累计最大值实现对特定形状的检测。

4.20.2霍夫变换的具体操作
4.20.2.1霍夫变换直线

cv.HoughLines(目标图像,r的精度,角度θ的精度,阈值)主要参数
目标图像:二值化,目标白,背景黑
r的精度:r的精度,以像素为单位,表示霍夫空间中每一步的距离增量, 值越大,考虑越多的线。
角度θ的精度:通常以弧度为单位,表示霍夫空间中每一步的角度增量。值越小,考虑越多的线。
阈值:满足目标斜率的函数图像上的点的最少数量

import cv2 as cv
import numpy as np
img=cv.imread('../images/huofu.png')#读取图片
img_g=cv.cvtColor(img,cv.COLOR_BGR2GRAY)#灰度化
dst=cv.Canny(img_g,30,70)#获得边缘检测后的图像
lines=cv.HoughLines(dst,0.9,np.pi/180,90)#进行霍夫直线变换
#返回ro值和角度theta值
print(lines)
for line in lines:
    ro = line[0][0]#提取ro
    theta= line[0][1]#提取角度
    sint_t=np.sin(theta)#计算cos和sin值
    cost_t=np.cos(theta)
    x1,x2=0,img.shape[1]#图的边界点
    y1,y2=int((ro-x1*cost_t)/sint_t),int((ro-x2*cost_t)/sint_t)#将边界点带入计算
    cv.line(img,(x1,y1),(x2,y2),(0,0,255),2)#绘制直线
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows

4.20.2.2统计概率霍夫直线变换

对于霍夫变换,对图像的预处理也尤其重要,若遇到明暗分布不均匀的图像,需要进行必要的处理
lines=cv2.HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=0, maxLineGap=0)**

image:输入图像,通常为二值图像,其中白点表示边缘点,黑点为背景。
rho:极径分辨率,以像素为单位,表示极坐标系中的距离分辨率。
theta:极角分辨率,以弧度为单位,表示极坐标系中角度的分辨率。
threshold:阈值,用于过滤掉弱检测结果,只有累计投票数超过这个阈值的直线才会被返回。
lines(可选):一个可初始化的输出数组,用于存储检测到的直线参数。
minLineLength(可选):最短长度阈值,比这个长度短的线会被排除。
maxLineGap(可选):同一直线两点之间的最大距离。当霍夫变换检测到一系列接近直角的线段时,这些线段可能是同一直线的不同部分。
注意:

  1. minLineLength 太小:可能会检测到很多短的线段,这些线段可能是噪声或不相关的边缘,增加计算量且降低检测结果的可靠性。
  2. minLineLength 太大:可能会漏掉一些较短但重要的线段,影响车道线的完整性。
  3. maxLineGap 太小:可能会导致一些连续的线段被分割成多段,无法正确表示实际的车道线。
    4.maxLineGap 太大:可能会将不相关的线段合并在一起,导致误检。
    对于公路识别原图:
import cv2 as cv    
import numpy as np
img=cv.imread('../images/daolu.jpg')
#降噪
img=cv.GaussianBlur(img,(3,3),0)
img_g=cv.cvtColor(img,cv.COLOR_BGR2GRAY)

dst=cv.Canny(img_g,50,80)
#统计概率霍夫直线变换
lines=cv.HoughLinesP(dst,1,np.pi/180,100,minLineLength=130,maxLineGap=50)#参数:图片,rho精度,theta精度,阈值,最小线段长度,最大线段间隔

#遍历取得数据
img_c=img.copy()
for line in lines:
    x1,y1,x2,y2=line[0]
    cv.line(img_c,(x1,y1),(x2,y2),(0,255,0),1,cv.LINE_AA)
cv.imshow('img_c',img_c)
cv.imshow('img',img)
cv.imshow('dst',dst)
cv.waitKey()
cv.destroyAllWindows()

4.20.2.3霍夫圆变换

circles=cv2.HoughCircles(image, method, dp, minDist, param1, param2)

image:输入图像,通常是灰度图像。

method:使用的霍夫变换方法:霍夫梯度法,可以是 cv2.HOUGH_GRADIENT,这是唯一在OpenCV中用于圆检测的方法。

dp:累加器分辨率与输入图像分辨率之间的降采样比率,用于加速运算但不影响准确性。设置为1表示霍夫梯度法中累加器图像的分辨率与原图一致

minDist:检测到的圆心之间的最小允许距离,以像素为单位。在霍夫变换检测圆的过程中,可能会检测到许多潜在的圆心。minDist 参数就是为了过滤掉过于接近的圆检测结果,避免检测结果过于密集。当你设置一个较小的 minDist 值时,算法会尝试找出尽可能多的圆,即使是彼此靠得很近的圆也可能都被检测出来。相反,当你设置一个较大的 minDist 值时,算法会倾向于只检测那些彼此间存在一定距离的独立的圆。

param1 和 param2:这两个参数是在使用 cv2.HOUGH_GRADIENT 方法时的特定参数,分别为:

param1(可选):阈值1,决定边缘强度的阈值。

param2:阈值2,控制圆心识别的精确度。较大的该值会使得检测更严格的圆。param2 通常被称为圆心累积概率的阈值。在使用霍夫梯度方法时,param2 设置的是累加器阈值,它决定了哪些候选圆点集合被认为是有效的圆。较高的 param2 值意味着对圆的检测更严格,只有在累加器中积累了足够高的响应值才认为是真实的圆;较低的 param2 值则会降低检测的门槛,可能会检测到更多潜在的圆,但也可能包含更多的误检结果。

返回值:cv2.HoughCircles 返回一个二维numpy数组,包含了所有满足条件的圆的参数。

import cv2 as cv
import numpy as np
img=cv.imread('../images/huofu.png')
imd_g=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
dst=cv.Canny(imd_g,30,70)
circles=cv.HoughCircles(dst,cv.HOUGH_GRADIENT,1,20,param2=30)
circles=np.int_(circles)
for i in circles:
    x,y,r=i[0]
    cv.circle(img,(x,y),r,(0,255,0),2)
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

4.21图像亮度变换

4.21.1步骤

读图

#读图
img=cv.imread('../images/cat1.png')

使用cv2.addweighted(sr1,alpha,sr2,beta,gamma)函数(输入图像,权重,输出图像,权重,偏移量(亮度))

将原图的所有像素值带入计算:

""" 
np.zeros_like(img):返回一个与img相同形状和类型的零数组。
np.ones_like(img):返回一个与img相同形状和类型的1数组。
np.full_like(img,value):返回一个与img相同形状和类型的value数组。
"""
dst=cv.addWeighted(img,1,np.zeros_like(img),1,200)

cv2.addWeighted(src1, alpha, src2, beta, gamma)**

  • src1:第一张输入图像,它将被赋予权重 alpha

  • alpha:第一个输入图像的权重。

  • src2:第二张输入图像,它将被赋予权重 beta

  • beta:第二个输入图像的权重。

  • gamma:一个标量,将被添加到权重求和的结果上,可用于调整总体亮度。

计算公式为: dst = src1 * alpha + src2 * beta + gamma

滑条控制图像

""" 
原图:img
输出:dst=img+p
防止溢出:np.clip(dst,0,255)
用一个滑条来控制p的值,p=0时,输出与原图相同,p=255时,输出全黑
"""

cv.createTrackbar(trackbarName,windowName,value,count,onChange)

clip=np.clip(img,50,180)

再利用np.clip修改数值小于所给的阈值,变成所给阈值,大于所给阈值,也修改为对应阈值

给滑条创建窗口

#给滑条创建窗口
win_name='trackbar'
cv.namedWindow(win_name)
def onChange(p):

再将滑条映射到[-255,255]

def onChange(p):
    #把滑条值映射到[-255,255]
    p=p/255*(255-(-255))-255
    #读图
    dst=np.uint8(np.clip(img+p,0,255))
    cv.imshow('img',img)
    cv.imshow('dst',dst)

设置初始参数

max_val=255#最大值
trackbar_name='p_value'#滑条名字
initial_value=150#初始值

初始化滑条

onChange(initial_value)

创建滑条

cv.createTrackbar(trackbar_name,win_name,0,max_val,onChange)
cv.waitKey(0)
cv.destroyAllWindows()

4.22形态学变换

4.22.1基本概念

形态学变换是基于图像形状的一系列操作,主要针对二值图像,使用结构元素(核)与图像进行相互作用。

4.22.2各运算总结
4.22.2.1. 腐蚀 (Erosion)
  • 作用:消除边界点,使物体缩小
  • 代码cv2.erode(src, kernel, iterations)
  • 步骤:核在图像上滑动,只有当核覆盖区域全为1时,中心像素才保留
4.22.2.2. 膨胀 (Dilation)
  • 作用:填补空洞,扩大物体
  • 代码cv2.dilate(src, kernel, iterations)
  • 步骤:核覆盖区域有至少一个1时,中心像素设为1
4.22.2.3. 开运算 (Opening|cv2.MORPH_OPEN)
  • 作用:先腐蚀后膨胀,去除小物体
  • 代码cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)
4.22.2.4. 闭运算 (Closing|cv2.MORPH_CLOSE)
  • 作用:先膨胀后腐蚀,填补小孔
  • 代码cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)
4.22.2.5. 礼帽运算/顶帽运算 (Top Hat|cv2.MORPH_TOPHAT)
  • 作用:原图与开运算结果之差
  • 代码cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel)
  • 实际用途:提取比背景亮的细小物体(如白纸上的黑字中的白噪声)
4.22.2.6. 黑帽运算 (Black Hat| cv2.MORPH_BLACKHAT)
  • 作用:闭运算结果与原图之差
  • 代码cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel)
  • 实际用途:提取比背景暗的细小物体(如黑纸上的白字中的黑点),工业检测中产品瑕疵
4.22.2.7. 形态学梯度 (Morphological Gradient|cv2.MORPH_GRADIENT)
  • 作用:膨胀与腐蚀结果之差,得到物体边缘
  • 代码cv2.morphologyEx(src, cv2.MORPH_GRADIENT, kernel)
4.22.3通用步骤
  1. 定义结构元素(核):kernel = cv2.getStructuringElement(shape, size)
  2. 选择适当的形态学操作
  3. 应用操作并显示结果

你可能感兴趣的:(opencv,人工智能,计算机视觉)