颜色的空间表达:
RBG:
CMYK:
HSI:
色调H是描述纯色的属性
饱和度S表示的是一种纯色被白光稀释的程度的度值
亮度体现了无色的光强度概念,是一种主观的描述
颜色空间分解:
在opencv中,颜色空间需要注意以下几点:
(1)opencv中,彩色图像缺省为BGR格式,颜色三通道的顺序为B、G、R,分别对应索引0,1,2
(2)opencv中,HSI/HSV颜色空间中的H的范围是0-180,是为了取得颜色的一个整数表示,其他两个是0-255
(3)两个相似概念:HSI和HSV空间。HSV里面的v指的是RGB里面的最大值,v = max(r,g,b);而HSI的I是平均值,I = (r+g+b)/3 ,另外两个分量是一样的。
对应程序实现:
import cv2 as cv
f = '/Users/loongc/Documents/love_dwr/computer_vision/lena.jpeg'
img = cv.imread(f)
cv.imshow('hello lena',img)
gray = cv.cvtColor(img,cv.COLOR_RGB2GRAY)
cv.imshow('gray',gray)
#cv.waitKey(0)
#cv.destroyAllWindows()
hsv = cv.cvtColor(img,cv.COLOR_RGB2HSV)
cv.imshow('hsv',hsv)
#cv.waitKey(0)
cv.imshow('Hue',hsv[:,:,0])
cv.imshow('Saturation',hsv[:,:,1])
cv.imshow('Value',hsv[:,:,2])
#cv.waitKey(0)
cv.imshow('Blue',img[:,:,0])
cv.imshow('Green',img[:,:,1])
cv.imshow('Red',img[:,:,2])
cv.waitKey(0)
对应程序实现:
import cv2 as cv
import numpy as np
f = '/Users/loongc/Documents/love_dwr/computer_vision/lena.jpeg'
img = cv.imread(f)
def gauss_noise(image,mean=0,var=0.001):
image = np.array(image/255,dtype = float)
noise = np.random.normal(mean,var**0.5,image.shape)
out = image+noise
if out.min()<0:
low_clip = -1
else:
low_clip = 0
out = np.clip(out,low_clip,1.0)
out = np.uint8(out*255)
return out
img = gauss_noise(img)
#平均滤波
blur = cv.blur(img,(5,5))
#高斯平滑滤波
gauss = cv.GaussianBlur(img,(5,5),0)
#中值滤波
median = cv.medianBlur(img,5)
#双边滤波器
bilateral = cv.bilateralFilter(img,5,150,150)
cv.imshow('hello lena',img)
cv.imshow('blur',blur)
cv.imshow('gauss',gauss)
cv.imshow('median',median)
cv.imshow('bilateral',bilateral)
cv.waitKey()
#形态学滤波
import cv2 as cv
import numpy as np
f = '/Users/loongc/Documents/love_dwr/computer_vision/lena.jpeg'
img = cv.imread(f)
def gauss_noise(image,mean=0,var=0.001):
image = np.array(image/255,dtype = float)
noise = np.random.normal(mean,var**0.5,image.shape)
out = image+noise
if out.min()<0:
low_clip = -1
else:
low_clip = 0
out = np.clip(out,low_clip,1.0)
out = np.uint8(out*255)
return out
nimg = gauss_noise(img)
kernel = cv.getStructuringElement(cv.MORPH_CROSS,(3,3))
eroded = cv.erode(nimg,kernel)
dilated = cv.dilate(nimg,kernel)
opened = cv.morphologyEx(nimg,cv.MORPH_OPEN,kernel)
closed = cv.morphologyEx(opened,cv.MORPH_CLOSE,kernel)
cv.imshow('noised image',nimg)
cv.imshow('eroded image',eroded)
cv.imshow('dilated image',dilated)
cv.imshow('opened image',opened)
cv.imshow('closed omage',closed)
cv.waitKey()
程序实现:
基础边缘检测算子代码实现:
import cv2 as cv
import numpy as np
f = '/Users/loongc/Documents/love_dwr/computer_vision/lena.jpeg'
img = cv.imread(f)
#Sobel算子边缘检测
sobel = cv.Sobel(img,cv.CV_16S,1,0,ksize = 3)#CV_16S表示目标图像所需深度16位有符号的整数
#Laplacian边缘检测
laplacian = cv.Laplacian(img,cv.CV_16S)
#Canny边缘检测,最小阈值50,最大阈值120
canny = cv.Canny(img,50,120)
sobel_show = cv.convertScaleAbs(sobel)#将16位有符号的图像转换成可显示的8位无符号的整数
lap_show = cv.convertScaleAbs(laplacian)
cv.imshow('sobel',sobel_show)
cv.imshow('laplacian',lap_show)
cv.imshow('canny',canny)
cv.waitKey()
类似于边缘检测结果。
顶帽操作:适用于原始图像背景是暗的,而前景是亮的。可以把明亮的前景突出出来
底帽操作:可以把明亮北京中的暗色的前景突出出来
顶帽和底帽操作结合使用:常用的做法是源图像加上顶帽变换再减去底帽变换,可以增强对比度
扩展的形态学变换的结果实现:
#以下分别计算并显示梯度、顶帽和黑帽变换结果
kernel = cv.getStructuringElement(cv.MORPH_CROSS,(3,3))
gradient = cv.morphologyEx(img,cv.MORPH_GRADIENT,kernel)
cv.imshow('image gradient',gradient)
tophat = cv.morphologyEx(img,cv.MORPH_TOPHAT,kernel)
bottomhat = cv.morphologyEx(img,cv.MORPH_BLACKHAT,kernel)
cv.imshow('tophat',tophat)
cv.imshow('blackhat',bottomhat)
#利用顶帽和黑帽变换进行图像增强,并显示结果
enhanced = img + tophat - bottomhat
cv.imshow('enhanced image',enhanced)
#计算击中或击不中(HMT)变换结果并显示
kernel_hmt = np.array(([0,1,0],[1,-1,1],[0,1,0]),dtype = 'int')
hmt = cv.morphologyEx(img,cv.MORPH_HITMISS,kernel_hmt)
cv.imshow('hit or miss transform',hmt)
cv.waitKey()
图像变换的意义:
使图像的特征更为突出;使原来无法直接观测的特征直接显现出来;需要提取图像中的特征,便于后续处理和图西那个理解。
图像几何变换:
图像放缩、图像平移、图像旋转、图像镜像、图像翻转
图像距离变换:
具有细化的效果
对数极坐标变换:
应用:全景展开
灰度直方图
直方图均衡化的作用就是图像增强,需要满足的条件是亮的依然亮,暗的依然暗。但会增强噪声
为了实现直方图均衡化,我们需要采用直方图重新映射的方法:
局部直方图均衡化:
改进局部直方图均衡化:
改进的局部直方图均衡结果:
霍夫变换:
总结:霍夫变换基于直线法线表示及对应的参数空间(@,p);最终直线参数确定采用投票机制,统计参数空间小格中的曲线数目。
程序实现:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
f = '/Users/loongc/Documents/love_dwr/computer_vision/lena.jpeg'
img = cv.imread(f)
#实现图像改变大小和翻转
w,h = img.shape[0:2]
resized = cv.resize(img,(int(w/4),int(h/2)))
fliped = cv.flip(img,-1)#此处-1、0、1实现上下左右翻转
#cv.imshow('resized',resized)
#cv.imshow('fliped',fliped)
#cv.waitKey()
#实现图像的距离变换
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,thr = cv.threshold(gray,100,255,cv.THRESH_OTSU)
#距离计算方式:DIST_L1、DIST_L2或DIST_C,实际中常用L2
#masksize:可取DIST_MASKPRECISE或DIST_MASK_3、5
dist = cv.distanceTransform(thr,cv.DIST_L2,cv.DIST_MASK_3)
dist_norm = cv.convertScaleAbs(dist)
#cv.imshow('dist_norm',dist_norm)
#cv.waitKey()
#实现Log_polar变换
center =(w/2,h/2)#变换中心
maxRadius = 0.7*min(center)
M = w/cv.log(maxRadius)#幅值尺度参数
print(maxRadius,M[0])
#系数越大,对应黑色区域越小
#把图像映射到极指数空间
log_polar = cv.logPolar(img,center,M[0]*0.8,cv.INTER_LINEAR+cv.WARP_FILL_OUTLIERS)
#CV_WARP_FILL_OUTLIERS 填充输出图像所有像素,如果这些点有和外点对应的,则置零
#cv_WARP_INVERSE_MAP 表示矩阵是从目标图像到源图像的反变换
#cv.imshow('log_polar',log_polar)
#cv.waitKey()
#实现灰度直方图和直方图均衡化
plt.hist(gray.ravel(),256,[0,256])
plt.show()
equa = cv.equalizeHist(gray)
#cv.imshow('equalized image',equa)
#cv.waitKey()
#实现Hough变换
edges = cv.Canny(thr,50,150)
disp_edge = cv.cvtColor(edges,cv.COLOR_GRAY2BGR)
lines = cv.HoughLinesP(edges,1,1*np.pi/180,10)
#10表示累加平面的阈值参数,超过设定阈值才被检测出线段,值越大,检测出的线段越长,检出的线段个数越少
for line in lines:
for x1,y1,x2,y2 in line:
cv.line(disp_edge,(x1,y1),(x2,y2),(0,255,0),1)
pass
print('line count',len(lines))
cv.imshow('disp_edge',disp_edge)
cv.waitKey()
基于灰度直方图的阈值分割
自动阈值分割方法-----大津(Otsu)算法
根据统计分析理论,最佳阈值确定的最佳二分类应使类内方差最小,等同于类间方差最大。于是,大津算法的基本思想:确定使灰度直方图类间方差最大的最佳阈。
大津算法原理:略
大津算法的求解采用遍历方式,思想直接,实现速度快。
基于边缘轮廓的分割
程序实例:
import numpy as np
import matplotlib.pyplot as plt
import copy
f = '/Users/loongc/Documents/love_dwr/computer_vision/rice.jpg'
img = cv.imread(f)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#大津算法灰度阈值化
thr,bw = cv.threshold(gray,0,0xff,cv.THRESH_OTSU)
#bw阈值后的二值图像,thr使用大津算法计算得出的阈值
print('Threshold is:',thr)
#画出灰度直方图
plt.hist(gray.ravel(),256,[0,256])
plt.show()
element = cv.getStructuringElement(cv.MORPH_CROSS,(3,3))
bw = cv.morphologyEx(bw,cv.MORPH_OPEN,element)
seg = copy.deepcopy(bw)
#计算轮廓
cnts,hier = cv.findContours(seg,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
#hier分割以后的结果,cnts分割完的所有轮廓存在其中
#cv.RETR_EXTERNAL只检测最外围的轮廓,cv.RETR_LIST检测所有轮廓,还有其他
#cv.CHAIN_APPROX_SIMPLE只保存轮廓的拐点信息
count = 0
for i in range(len(cnts),0,-1):
c = cnts[i-1]
area = cv.contourArea(c)
if area < 10:
continue
count = count + 1
print('blob',i,':',area)
#区域画框并标记
x,y,w,h = cv.boundingRect(c)
cv.rectangle(img,(x,y),(x+w,y+h),(0,0,0xff),1)
cv.putText(img,str(count),(x,y),cv.FONT_HERSHEY_PLAIN,0.5,(0,0xff,0))
print('米粒数量:',count)
cv.imshow('原图',img)
cv.imshow('阈值化图',bw)
cv.waitKey()
阈值化分割扩展分割方法----局部阈值法
|
|
|
阈值化分割扩展方法----多阈值分割法
|
|
|
基于区域的分割
基于区域的分割-----区域生长法:
区域生长:从种子点开始,按照一定规则(如相邻像素灰度相似性)向周围扩散,将领域相似像素加入区域中,
搜索扩散有两种方式:深度优先、广度优先
基于区域的分割-----区域分裂合并法:
区域分裂合并实现步骤:对区域分裂合并法无需预先指定种子点,它按某种一致性准则分裂或者合并区域;可以先进性分裂运算,然后再进行合并运算,也可以分裂和合并运算同时进行,经过连续的分裂和合并,最后得到图像的精确分割效果;分裂合并法对分割复杂的场景图像比较有效。
总结: 区域生长法基于相邻像素间的相似性,由种子像素逐步生长得到;分裂合并基于图像块内在的相似性,通过不断分裂得到区域外边界,通过合并将不同块连接.
分水岭算法分割
由于噪声点或者其他干扰因素的存在,使用分水岭算法常常存在过度分割的现象,这是因为有很多局部极小值点的存在。
|
|
|
|
为了解决过度分割的问题,可以使用基于标记mark图像的分水岭算法,就是指定mark图像,在这个区域洪水淹没过程中,水平面都是从定义的marker开始的,这样可以避免一些很小的噪声极值区域的分割。
图像分割算法程序实现
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
import copy
f = '/Users/loongc/Documents/love_dwr/computer_vision/coins.jpg'
img = cv.imread(f)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#大津算法做自适应阈值化,作为预处理的第一步
ret,thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
print('threshold is:',ret)
#对阈值图像做开运算,去除一些白色小的噪声点
kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel,iterations = 2)
#再做膨胀运算,使用膨胀运算确保背景与原图一致
sure_bg = cv.dilate(opening,kernel,iterations = 3)
#cv.imshow('gray',gray)
cv.imshow('thresh',thresh)
cv.imshow('opening',opening)
cv.imshow('sure_bg',sure_bg)
#cv.waitKey()
#做距离变换计算每个硬币中心,鲁棒性更好
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
ret,sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0)
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)
cv.imshow('dist_transform',dist_transform)
cv.imshow('sure_fg',sure_fg)
cv.imshow('unknown',unknown)
#cv.waitKey()
ret,markers = cv.connectedComponents(sure_fg)#为了确定有多少个目标
markers = markers+1#标记+1,让标记从1开始
markers[unknown==255]=0#不确定区域设置为0,便于下一步分割
markers = cv.watershed(img,markers)#使用watershed做分水岭分割
img[markers == -1] = [255,0,0]#分割线用蓝色标记
cv.imshow('markers',markers)
cv.imshow('img',img)
cv.waitKey()
简单描述----边界描述
|
||
|
|
|
简单描述-----区域描述
|
|
|
|
一般描述
程序实现
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
import copy
f = '/Users/loongc/Documents/love_dwr/computer_vision/circle.jpg'
img = cv.imread(f)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#首先对图像进行阈值化
_,thr = cv.threshold(gray,200,255,cv.THRESH_BINARY)
cv.imshow('img',gray)
#cv.imshow('threshold image',thr)
#cv.waitKey()
#进一步得到图像的轮廓
cnts,hier = cv.findContours(thr,cv.RETR_LIST,cv.CHAIN_APPROX_NONE)
count = 0
#用于显示的图像
disp_poly = img.copy()
disp_elli = img.copy()
for i in range(len(cnts)):
c = cnts[i]
poly = cv.approxPolyDP(c,5,True)#5,参数越大拟合允许误差越大
cv.polylines(disp_poly,[poly],True,(255,255,255),2)
#拟合椭圆
if (len(c)>5):
ellipse = cv.fitEllipse(c)
cv.ellipse(disp_elli,ellipse,(255,255,255),2)
#计算hu不变矩
area = cv.contourArea(c)
length = cv.arcLength(c,True)
moments = cv.moments(c)
hu = cv.HuMoments(moments)
print(i+1,':','length is %.1f'%length,'area =',area,
'm00=%.3f,m01=%.3f,m10=%.3f,m11=%.3f,'%(hu[0],hu[1],hu[2],hu[3]))
x,y,w,h = cv.boundingRect(c)
cv.putText(disp_poly,str(i+1),(x,y),cv.FONT_HERSHEY_PLAIN,0.8,(0xff,0xff,0xff))
cv.imshow('disp_poly',disp_poly)
cv.imshow('disp_elli',disp_elli)
cv.waitKey()
图像滤波:卷积和滤波的概念、图像平滑去燥,图像边缘检测,使用形态学滤波可以完成特征提取、边缘检测、图像增强
灰度直方图,hough变换与直线检测,道路检测,图像边缘检测,在自动驾驶时非常实用。使用距离变换可以分开互相粘连的图像。
图像分割与描述:基于灰度直方图的阈值分割(分离前后背景差异比较明显),基于区域生长法(渐变图像),分水岭算法
图像的描述:
机器视觉:生产线缺陷检测,零件识别、计数等
图像处理:图像美颜、图像风格定制化、图像增强等
医学图像处理:细胞分类、异常组织间的、分割等
卫星与航空遥感图像处理:特定目标检测、识别等