python学习笔记(持续更新)

一些小tips:

 

python学习笔记(持续更新)_第1张图片

python学习笔记(持续更新)_第2张图片

文件夹必须这样放

 

一、图像的基本操作

1.jupyter中 shift+回车 可以直接运行一个模块

1.2灰度图中0最暗是黑色 255最亮

1.5

1 import cv2
2 import matplotlib.pyplot as plt
3 import numpy as np
4 %matplotlib inline

这些是常用的包的导入

2.import as的用法:

在Python中,如果import的语句比较长,导致后续引用不方便,可以使用as语法,比如:

import dir1.dir2.mod

# 那么,后续对mod的引用,都必须是dir1.dir2.mod
dir1.dir2.mod.X
那么,为了简化输入,可以使用as语法:

import dir1.dir2.mod as m

# 那么,后续对mod的引用,可以直接使用m
m. X

# 需要注意的是,使用as语法之后,只能通过as后面名字来访问导入的moudle
import mod as m
m.X  # OK
mod.X # Error

3.opencv默认读取图片格式是BGR(三通道,范围是0-255)

4.读取图像命令

img=cv2.imread('cat.jpg') 读取图片 并且是本路径下的图片

5.显示图像命令

cv2.imshow=('img1',img)  第一个参数是显示图像必须要临时取的名字,第二个是图像的定义名(即4中的img)

cv2.waitKey(数值) 即等待时间,毫秒级。例如cv2.waitKey(1000),即图片显示后等待1s,数值若为0,则表示任意键终止 配合cv2.destoryallwindows()这条使用

6

img.shape

运行此命令后显示(414,530,3) 这三个参数不固定,显示的是h w 和图片的通道数3

7.读取灰度图

img=cv2.imread(‘cat.jpg’,cv2.IMREAD_GRAYSCALE)

这时读取的就是灰度图,运行img.shape后得到的就是(414,530)  表示没有三通道了,只是一个灰度图

8.保存图片

cv2.imwrite(‘mycat.png’,img)

9.img.size运行后得到20700表示此图片有20700个像素点组成

10.img.dtype运行后得到dtype(‘uint8’)表示储存这些像素值的数字数uint8类型。 dtype表示数据类型

11.读取视频的一系列操作

vc=cv2.VideoCapture('test.mp4')  #传入视频流

#检测视频能否正常读取

if vc.isOpened():

    open,frame=vc.read()

else:

    open=False

其中read()是一帧一帧地读取视频中的图片,返回的第一个参数open是bool值,用来表示视频是否正常打开,第二个参数frame保存的是每一帧的图片

 

while open:

    ret,frame=vc.read()

   if frame is None:

        break;

    if ret==true;

        gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)    #把frame传进去进行2gray的操作

        cv2.imshow('result',gray)

        if  cv2.waitKey(10) & 0xFF==27:     #27代表ESC ,waitKey代表处理完一帧等待的时间(毫秒级)

            break;

vc.release()

cv2.destroyALLWindows()

12.截取部分图像

img=cv2.imread(‘cat.jpg’)

cat=img[0:50,0:200]

cv2.imread('cat',cat)

13.颜色通道提取

b.g.r=cv2.split(img)执行后再执行一个单独的

r

就会显示r通道上所有的像素值

r.shape运行后得到的还是(414,500)  两外俩结果也一样 都跟灰度图的h w保持一致

分开之后可以把这三种颜色merge在一起

img=cv2.merge(b,g,r)

然后运行img.shape之后得到还是(414,500,3)的结果,表明三通道重新merge在一起了

14.只保留R通道

cur_img=img.copy()

cur_img[:,:,0]=0  #第一个通道即B通道所有数为0

cur_img[:,:,1]=0  #第二个通道即G通道所有数为0

cv2.imread('R',cur_img)  #显示只有R通道时候的图像

##只保留G通道的代码

cur_img=img.copy()

cur_img[:,:,0]=0  #第一个通道即B通道所有数为0

cur_img[:,:,2]=0  #第二个通道即R通道所有数为0

cv2.imread('G',cur_img)  #显示只有G通道时候的图像

15.边界填充

top_size,bottom_size,left_size,right_size=(50,50,50,50)  #定义四个变量

replicate=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REPLICATE)

reflect=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REFLECT)

reflect101=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REFLECT_101)

wrap=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_WRAP)

constant=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_CONSTANT,value=0)

以上为五种边界填充的方法:

BORDER_REPLICATE:复制法,直接复制最边缘的像素

BORDER_REFLECT:反射法,对图像中的像素在两边进行复制 如:fedcba|abcdefgh|hgfedcb

BORDER_REFLECT_101:反射法,以最边缘的像素为轴进行反射  如:fedcb|abcdefgh|gfedcb  相对上面这种方法少了重复的部分,会更加的对称,看上去更加自然一点

BORDER_WRAP:外包装法 如cdefgh|abcdefgh|abcdefg  这个还有些不太懂。。。

BORDER_CONSTANT:常量法,使用常数值填充,会多一个value变量

#显示图像

import matplotlib.pyplot as plt

plt.subplot(231),plt.imshow(img,'gray'),plt.title('ORIGINAL')  

plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')  

...

plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')  

就会六张显示在同一张图中

subplot中的三个参数 第一个表示行数 第二个表示列数 第三个表示是第几张图片

16.数值计算

img_cat=cv2.imread('cat.jpg')

img_dog=cv2.imread('dog.jpg')  #先读两张图

img_cat2=img_cat+10  #假如图像中为500*500 且是rgb三通道的,那么此操作意味在500*500*3的每一个数都加10

img_cat+img_cat2  #若使用img_cat+img_cat2操作,如果计算结果超过了255,那么其显示结果不会超过255,相当于%256

opencv提供了cv2.add函数

cv2.add(img_cat,img_cat2)  #opencv提供了cv2.add函数则是不同算法,如果计算结果大于255的话不会像上述计算过程一样取余,超过255的结果就按照255来,没越界就用计算结果

img_cat+img_dog  #图像融合操作,前提是两张图片size一样

img_dog=cv2.resize(img_dog,(500,414))  #输入想变换的图像  resize操作 参数顺序为w h

执行img_dog.shape之后显示的是(414,500,3)#显示的是h w 通道数

res=cv2.resize(img,(0,0),fx=3,fy=1)  #w h参数为0 0表示不指定具体参数,后面多的fx表示为原图w的多少倍,fy表示h为原图的多少倍 即像素数为原来的几倍

res=cv2.addWeighted(img_cat,0.4,img_dog,0.6,0)  #R=X*0.4+Y*0.6+b  这五个参数分别是x 0.4 y 0.6 0(偏置项) 偏置项一般来提亮或者变暗

二、阈值与平滑处理

1.图像阈值
ret, dst = cv2.threshold(src, thresh, maxval, type)  #等号左边的两个返回参数第一个还不知道什么用,第二个参数可以改成你想要的图像名字
src: 输入图,只能输入单通道图像,通常来说为灰度图
dst: 输出图
thresh: 阈值  常用的有127
maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值  灰度的话这个值设为255
type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINRY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0
cv2.THRESH_BINARY_INV THRESH_BINARY的反转
cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转

2.图像平滑

#均值滤波

#简单的平均卷积操作

blur = cv2.blur(img, (3, 3))  #img表示被处理的图像 (3,3)表示核的尺寸,通常用奇数*奇数  blur是均值滤波函数

 

#方框滤波

#可以选择归一化 归一化之后和均值滤波一样

box=cv2.boxFilter(img,-1,(3,3),normalize=True)  #img是原图  -1基本不会变  (3,3)是核尺寸 true表示选择了归一化(3*3个像素值加载一起要除以9)

cv2.imshow('box',box)

cv2.WaitKey(0)

cv2.destoryAllWindows()

#选择不归一化(所有超过255的越界值最后赋予255)

box=cv2.boxFilter(img,-1,(3,3),normalize=False)  #img是原图  -1基本不会变  (3,3)是核尺寸 False表示没有选择归一化(没有处以9)

cv2.imshow('box',box)

cv2.WaitKey(0)

cv2.destoryAllWindows()

 

#高斯滤波

 # 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(img, (5, 5), 1)   #最后一个参数是sigma标准差

cv2.imshow('aussian',aussian)

cv2.WaitKey(0)

cv2.destoryAllWindows()

 

#中值滤波

# 相当于用中值代替*
median = cv2.medianBlur(img, 5)    #表示核尺寸是5*5的,选择第13大的数字(中值、中位数)

 

#展示所有

res=np.hstack((blur,aussian,median))   #hstack是横着展示 horizontal 水平的  vstack是竖着展示  vertial垂直的;注意双层括号

cv2.imshow('median vs average',res)

cv2.WaitKey(0)

cv2.destoryAllWindows()

 

三、腐蚀(erode)操作(去毛刺)和膨胀(dilate)操作

核尺寸越大 可能被腐蚀的范围就越多(因为核越大 那么核边界接触背景的时候,核中心很靠内)

#腐蚀

img=cv2.imread('dige.jpg')

cv2.imshow('img',img)

cv2.WaitKey(0)

cv2.destoryAllWindows()

python学习笔记(持续更新)_第3张图片

kernel=np.ones((5,5),np.uint8)  #定义核 尺寸5*5 全是数字1 类型是uint8

erosion = cv2.erode(img,kernel,iterations = 1) #iterations 迭代次数

cv2.imshow('erosion',erosion)

cv2.WaitKey(0)

cv2.destoryAllWindows()  #显示图像

python学习笔记(持续更新)_第4张图片

 

 

#迭代次数的不同

python学习笔记(持续更新)_第5张图片

 

#膨胀(与腐蚀互为逆运算)

kernel = np.ones((3,3),np.uint8)
dige_dilate = cv2.dilate(dige_erosion,kernel,iterations = 1)  #膨胀

 

 

#膨胀次数的不同

python学习笔记(持续更新)_第6张图片

 

#开运算与闭运算

开运算:先腐蚀,后膨胀

闭运算:先膨胀,后腐蚀

#开运算(先干掉毛刺,再恢复原状)

img = cv2.imread('dige.png')

kernel = np.ones((5,5),np.uint8) 
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()

python学习笔记(持续更新)_第7张图片

开运算之后

python学习笔记(持续更新)_第8张图片

#闭运算

img = cv2.imread('dige.png')

kernel = np.ones((5,5),np.uint8) 
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

cv2.imshow('closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()

python学习笔记(持续更新)_第9张图片

闭运算之后(毛刺膨胀之后变粗就不容易再腐蚀掉了)

python学习笔记(持续更新)_第10张图片

 

#梯度运算

##梯度=膨胀-腐蚀
pie = cv2.imread('pie.png')
kernel = np.ones((7,7),np.uint8) 
dilate = cv2.dilate(pie,kernel,iterations = 5)
erosion = cv2.erode(pie,kernel,iterations = 5)

res = np.hstack((dilate,erosion))

cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()  #同时显示膨胀和腐蚀的效果

python学习笔记(持续更新)_第11张图片

 

##梯度运算代码

gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)  ##减小kernel大小可以使得边界更细更精确

cv2.imshow('gradient', gradient)

cv2.waitKey(0)

cv2.destroyAllWindows()  #运行结果是膨胀-腐蚀

python学习笔记(持续更新)_第12张图片

 

#礼帽和黑帽

礼帽=原始输入-开运算结果
黑帽 = 闭运算-原始输入

#礼帽  礼帽得到的是刺 即被腐蚀的部分 保留刺 去掉图
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()

python学习笔记(持续更新)_第13张图片

 

#黑帽  剩下原图的一些轮廓   刺-刺=没了
img = cv2.imread('dige.png')
blackhat  = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat ', blackhat )
cv2.waitKey(0)
cv2.destroyAllWindows()

python学习笔记(持续更新)_第14张图片

 

 

 

#边缘检测

sobel算子

python学习笔记(持续更新)_第15张图片

img=cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)

cv2.imshow('img',img)

cv2.waitKey(0)

cv2.destroyAllWindows()

 

dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
ddepth:图像的深度
dx和dy分别表示水平和竖直方向
ksize是Sobel算子的大小

#这个算子算出来小于0的数全截断为0了

 

##注释下ddepth的意义

图像深度是指存储每个像素所用的位数,也用于量度图像的色彩分辨率.图像深度确定彩色图像的每个像素可能有的颜色数,或者确定灰度图像的每个像素可能有的灰度级数.它决定了彩色图像中可出现的最多颜色数,或灰度图像中的最大灰度等级.比如一幅单色图像,若每个象素有8位,则最大灰度数目为2的8次方,即256.一幅彩色图像RGB3个分量的象素位数分别为4,4,2,则最大颜色数目为2的4+4+2次方,即1024,就是说像素的深度为10位,每个像素可以是1024种颜色中的一种.

例如:一幅画的尺寸是1024*768,深度为16,则它的数据量为1.5M。

计算如下:102476816bit=(102476816)/8字节=[(102476816)/8]/1024KB={[(102476816)/8]/1024}/1024MB。

深度组合
输入深度(src.depth()) 输出深度(ddepth)
CV_8U -1 / CV_16S / CV_32F / CV_64F
CV_16U / CV_16S -1 / CV_32F / CV_64F
CV_32F -1 / CV_32F / CV_64F
CV_64F -1 / CV_64F
注意
当ddepth = -1时,输出图像将具有与源相同的深度。

##################

 

sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)  #cv2.CV_64F 正负都能表示

def cv_show(img,name):

    cv2.imshow(name,img)

    cv2.waitKey()

    cv2.destroyAllWindows()   ##定义一个显示函数

cv_show(sobelx,'sobelx')

python学习笔记(持续更新)_第16张图片

 

上图右边是缺失的;白(255)到黑(0)是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值

sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)  ##这个函数可能在不同版本中升级可能会直接呈现下面图中的结果

sobelx = cv2.convertScaleAbs(sobelx)    #绝对值

cv_show(sobelx,'sobelx')

python学习笔记(持续更新)_第17张图片

 

sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)

sobely = cv2.convertScaleAbs(sobely)  #求绝对值

cv_show(sobely,'sobely')    #计算y方向的

python学习笔记(持续更新)_第18张图片

 

分别算出Gx和Gy之后,可以算出G  一般G=根号下(Gx^2+Gy^2) 或者G=|Gx|+|Gy|

sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)    #dst = src1 * alpha + src2 * beta + gamma;

cv_show(sobelxy,'sobelxy')

python学习笔记(持续更新)_第19张图片

 

分别计算x和y,再求和;不建议直接计算(效果不如分开计算)

sobelxy = cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)

sobelxy = cv2.convertScaleAbs(sobelxy)  #求绝对值

cv_show(sobelxy,'sobelxy')    #同时计算xy方向的

python学习笔记(持续更新)_第20张图片

 

#Scharr算子

python学习笔记(持续更新)_第21张图片

数值比sobel算子更大一些 对结果的差异更明显更敏感一些(上图中Gy写错了 下面三行应该是+3 +10 +3)

 

#laplacican算子

python学习笔记(持续更新)_第22张图片

并不会单独去使用,因为拉普拉斯算子对噪音更加敏感一些,会跟其他工具结合使用

 

#不同算子的差异
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)   
sobely = cv2.convertScaleAbs(sobely)  
sobelxy =  cv2.addWeighted(sobelx,0.5,sobely,0.5,0)  

scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)   
scharry = cv2.convertScaleAbs(scharry)  
scharrxy =  cv2.addWeighted(scharrx,0.5,scharry,0.5,0) 

laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)   

res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,'res')

python学习笔记(持续更新)_第23张图片

scharry效果更好

 

 

#Canny边缘检测

1.使用高斯滤波器,以平滑图像,滤除噪声。

2.计算图像中每个像素点的梯度强度和方向。

3.应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。

4.应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。

5.通过抑制孤立的弱边缘最终完成边缘检测。

python学习笔记(持续更新)_第24张图片

python学习笔记(持续更新)_第25张图片

python学习笔记(持续更新)_第26张图片

python学习笔记(持续更新)_第27张图片

###梯度值在minVal和maxVal中间的时候,如果连有边界,那么就认为其是边界####

 

img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)

v1=cv2.Canny(img,80,150)     #minVal80,maxVal150  当minVal较小的时候,降低了淘汰标准,意为检测出的边界越多越好,但是可能检测出来的可能不只是边                                                     界还有一些其他的,当minVal较大的时候,只有该点极有可能是边界的情况下才会被保留。。maxVal同理
v2=cv2.Canny(img,50,100)

res = np.hstack((v1,v2))
cv_show(res,'res')

参数不同得到的结果  参数越小得到的纹理越多 因为阈值上面的都是留下来的点

python学习笔记(持续更新)_第28张图片

 

#图像处理

图像金字塔 

高斯金字塔 拉普拉斯金字塔

python学习笔记(持续更新)_第29张图片

高斯金字塔:向下采样方法(缩小)从下往上 相当于卷积     #向下采样当中向下的意思是图像大小慢慢变小

python学习笔记(持续更新)_第30张图片

将偶数行和列去除后会使得800*800的图像变为400*400的图像  大小变为原来的1/4

 

高斯金字塔:向上采样方法(放大)

python学习笔记(持续更新)_第31张图片

注意:放大和缩小都会带来部分信息丢失

 

 

img=cv2.imread("AM.png")
cv_show(img,'img')
print (img.shape)
结果是(442, 340, 3) 

图片显示为

python学习笔记(持续更新)_第32张图片
up=cv2.pyrUp(img)    #pyramid 金字塔  放大
cv_show(up,'up')
print (up.shape)
结果显示为(884, 680, 3)

python学习笔记(持续更新)_第33张图片

down=cv2.pyrDown(img)
cv_show(down,'down')
print (down.shape)
结果显示为(221, 170, 3)

python学习笔记(持续更新)_第34张图片

 

 

拉普拉斯金字塔

python学习笔记(持续更新)_第35张图片

Gi表示原来的图

过程图为可以连续操作好几次的过程图

down=cv2.pyrDown(img)
down_up=cv2.pyrUp(down)
l_1=img-down_up
cv_show(l_1,'l_1')
python学习笔记(持续更新)_第36张图片

 

 

#图像轮廓

cv2.findContours(img,mode,method) 轮廓是一个整体


mode:轮廓检索模式 Contours轮廓

RETR_EXTERNAL :只检索最外面的轮廓;
RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次; (最常用)

 

method:轮廓逼近方法(最常用两种)

  • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
  • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。

python学习笔记(持续更新)_第37张图片

####第二种用四个点就可以表示一个长方形

为了更高的准确率,使用二值图像,轮廓检测之前要把图像化为二值图像

img = cv2.imread('car.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)    #二值处理

cv_show(thresh,'thresh')

显示结果为下图(二值图像)

python学习笔记(持续更新)_第38张图片

 


#binary二值后图片;contours数据点是保存轮廓的信息 使用np.array(contours)可以看到;hierarchy表示层级 
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)   #新版opencv好像得把binary去掉就可以了#opencv2返回contours, hierarchy,opencv3返回binary, contours, hierarchy


#绘制轮廓
#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
# 注意需要copy,要不原图会变
draw_img = img.copy()   #先copy一下,因为下面的.drawContours函数会把它处理的图像变化一下并保存下来  叫做传递对象的引用 python不少函数这样子
#-1代表所有轮廓 也可以为0 此时会显示第一个轮廓;  (0, 0, 255)红色BGR;  2 线条宽度
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
cv_show(res,'res')

显示结果如下图

python学习笔记(持续更新)_第39张图片

 

#轮廓特征

cnt = contours[0]    #第一个轮廓  contours为上面计算出来的
#面积
cv2.contourArea(cnt)#  8500.5
#周长,True表示闭合的
cv2.arcLength(cnt,True)#437.9482651948929
想要计算上述特征的话需要计算各个轮廓的特征

 

#轮廓近似

python学习笔记(持续更新)_第40张图片

#轮廓近似原理

AB曲线中找到一点C 使得该点到直线AB的距离d1最大,看下d1是否超过了界限T,如果没有超过那么AB弧会被线段AB代替,如果超过了T,那么分别按照这种方法来分析AC弧和BC弧,即对于AC弧观察d2是否超过了T,BC弧同理。  

img = cv2.imread('contours2.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]

draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')
python学习笔记(持续更新)_第41张图片

 

epsilon = 0.15*cv2.arcLength(cnt,True)      # 0.15这个系数越大,轮廓近似图越小
approx = cv2.approxPolyDP(cnt,epsilon,True)   #第一个参数是某一个轮廓;第二个参数是一个值,就是上述界限T,一般按照周长的一个比例来做;第三个参数

draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
 

 

#边界矩形 (即包含住图形的最小矩形)

img = cv2.imread('contours.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]

x,y,w,h = cv2.boundingRect(cnt)    # #这个函数很简单,img是一个二值图,也就是它的参数;返回四个值,分别是x,y,w,h;x,y是矩阵左上点的坐标,w,h是矩阵的宽和高,然后利用cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)画出矩形
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)   ##rectangle用一个最小的矩形,把找到的形状包起来

cv_show(img,'img')

python学习笔记(持续更新)_第42张图片

 

#计算一些特征 比如面积比

area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print ('轮廓面积与边界矩形比',extent)
#out:0.5154317244724715
 

#外接圆

(x,y),radius = cv2.minEnclosingCircle(cnt) 
center = (int(x),int(y)) 
radius = int(radius) 
img = cv2.circle(img,center,radius,(0,255,0),2)
cv_show(img,'img')
 

#模板匹配

 

python学习笔记(持续更新)_第43张图片

模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)

TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
TM_CCORR:计算相关性,计算出来的值越大,越相关
TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
尽量用带归一化的结果
公式:匹配公式https://docs.opencv.org/3.3.1/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d

 

# 模板匹配
img = cv2.imread('lena.jpg', 0)
template = cv2.imread('face.jpg', 0)
h, w = template.shape[:2] 
#img.shape (263, 263)
#template.shape (110, 85)
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
           'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
res.shape    #模板匹配结果个数(154, 179),即原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)

min_val,max_val,min_loc,max_loc=cv2.minMaxLoc(res)    #相当于四个参数分别是模板与原图最小差值,最大差值以及他们所在的位置,这个min_loc位置是用那个框的左上角的点来表示的,知道模板的长度宽度就可以画出这个框

运行min_val得到39168.0

运行max_val得到74403584.0

运行min_loc得到(107,89)  左上角为原点 从左到右 从上到下

运行max_loc得到(159,62)

 

#六种结果的差异图

for meth in methods:
    img2 = img.copy()

    # 匹配方法的真值
    method = eval(meth)
    print (method)
    res = cv2.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    # 如果是平方差匹配TM_SQDIFF或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)

    # 画矩形
    cv2.rectangle(img2, top_left, bottom_right, 255, 2)

    plt.subplot(121), plt.imshow(res, cmap='gray')
    plt.xticks([]), plt.yticks([])  # 隐藏坐标轴
    plt.subplot(122), plt.imshow(img2, cmap='gray')
    plt.xticks([]), plt.yticks([])
    plt.suptitle(meth)
    plt.show()

带归一化的操作结果一般不会差

python学习笔记(持续更新)_第44张图片python学习笔记(持续更新)_第45张图片python学习笔记(持续更新)_第46张图片python学习笔记(持续更新)_第47张图片

 

python学习笔记(持续更新)_第48张图片python学习笔记(持续更新)_第49张图片

 

 

 

 

匹配多个对象

img_rgb = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
h, w = template.shape[:2]

res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8    #指定的阈值
# 取匹配程度大于%80的坐标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):  # *号表示可选参数
    bottom_right = (pt[0] + w, pt[1] + h)
    cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)     #网友注:loc[::-1]可以反转获取到坐标点顺序,zip(*A)可以把A元组转为二维矩阵,因为np.where返回的是元祖

cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
#out -1
 

#直方图和傅里叶变换

python学习笔记(持续更新)_第50张图片

 

cv2.calcHist(images,channels,mask,histSize,ranges)

  • images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img],通常情况下都是灰度图输入的
  • channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。
  • mask: 掩模图像。统整幅图像的直方图就把它为 None。但是如 果你想统图像某一部分的直方图的你就制作一个掩模图像并 使用它。简单说就是只看某一块的数据时候用掩码
  • histSize:BIN (就是直方图的块数)的数目。也应用中括号括来
  • ranges: 像素值范围常为 [0,256]
import cv2 #opencv读取的格式是BGR
import numpy as np
import matplotlib.pyplot as plt#Matplotlib是RGB
%matplotlib inline 
def cv_show(img,name):
    cv2.imshow(name,img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
img = cv2.imread('cat.jpg',0) #0表示自动转为灰度图
hist = cv2.calcHist([img],[0],None,[256],[0,256])
# hist.shape (256, 1)
plt.hist(img.ravel(),256); #img.ravel是多维矩阵变一维数组
plt.show()

python学习笔记(持续更新)_第51张图片

img = cv2.imread('cat.jpg') 
color = ('b','g','r')
for i,col in enumerate(color): 
    histr = cv2.calcHist([img],[i],None,[256],[0,256]) 
    plt.plot(histr,color = col) 
    plt.xlim([0,256]) 

python学习笔记(持续更新)_第52张图片

 

Mask(掩码)操作

# 创建mask
mask = np.zeros(img.shape[:2], np.uint8)  #[:2]表示取前两个值,[:]代表取所有值,shape是一维数组,取前两个参数w,h
print (mask.shape)
mask[100:300, 100:400] = 255    #255是默认要求,范围是可变的
#cv_show(mask,'mask') (414, 500)
img = cv2.imread('cat.jpg', 0)
masked_img = cv2.bitwise_and(img, img, mask=mask)    #与操作
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask, 'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0, 256])
plt.show()
#1 为原图 2 为mask图 3 为masked_img  4为直方图

python学习笔记(持续更新)_第53张图片

直方图均衡化

python学习笔记(持续更新)_第54张图片

python学习笔记(持续更新)_第55张图片

下图左为原始像素点,右边为映射后的像素点

python学习笔记(持续更新)_第56张图片

累积概率相当于概率密度(小于等于改值);映射之后间距变小,其真正含义是均衡化希望对于不同的自变量值,自变量每向前走一步,步长一定的情况下,因变量的增量不会相差太远。

原图&&均衡化

img = cv2.imread('clahe.jpg',0) #0表示灰度图 #clahe
plt.hist(img.ravel(),256); 
plt.show()

python学习笔记(持续更新)_第57张图片

equ = cv2.equalizeHist(img)   #均衡化函数
plt.hist(equ.ravel(),256)
plt.show()

python学习笔记(持续更新)_第58张图片

右边图均衡化之后丢失了部分细节,看起来更清晰

python学习笔记(持续更新)_第59张图片

自适应直方图均衡化
分成很多小块各自的进行均衡化
 

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
res_clahe = clahe.apply(img)
res = np.hstack((img.equ.res_clahe))
cv_show(res,'res')

python学习笔记(持续更新)_第60张图片

分别是原始图像、全局均衡化、自适应均衡化。想要保留一些细节就要自适应,但是受噪点影响大。

#傅里叶变换

我们生活在时间的世界中,早上7:00起来吃早饭,8:00去挤地铁,9:00开始上班。。。以时间为参照就是时域分析。

但是在频域中一切都是静止的!

https://zhuanlan.zhihu.com/p/19763358

python学习笔记(持续更新)_第61张图片

上图频域图像中 纵坐标为振幅,横坐标为频率!(这个回答看过好几次了,写的真好)

 

傅里叶变换的作用

  • 高频:变化剧烈的灰度分量,例如边界

  • 低频:变化缓慢的灰度分量,例如一片大海

滤波

  • 低通滤波器:只保留低频,会使得图像模糊    #高频没了,图像边界会变得模糊
  • 高通滤波器:只保留高频,会使得图像细节增强    #相当于锐化了
  • opencv中主要就是cv2.dft()和cv2.idft()(逆变化才能显示出来),输入图像需要先转换成np.float32 格式。
  • cv2.dft()得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现。 #低频区拉到中心

 

  • cv2.dft()返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能展示(0,255)。

​
import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('lena.jpg',0) #灰度

img_float32 = np.float32(img) #转换格式

dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)#低频的部分转换到中间位置
# 得到灰度图能表示的形式
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1])) #需要这样转化一下才可以得到图像形式的表达;分别对两个通道进行转换;通过*20得到0到255之间的数值

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

​

python学习笔记(持续更新)_第62张图片

结果如上图,中间是低频。(掩码取中间部分,就叫低通滤波;盖住中间取外边部分,叫做高通滤波器)

#低通

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('lena.jpg',0)

img_float32 = np.float32(img)

dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

rows, cols = img.shape
crow, ccol = int(rows/2) , int(cols/2)     # 中心位置

# 低通滤波
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1

# IDFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)    #执行idft之前要先执行ishift,留下来中间区域
img_back = cv2.idft(f_ishift)    #这时候还是实部虚部的双通道的结果
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])    #对实部虚部进行处理

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Result'), plt.xticks([]), plt.yticks([])

plt.show()                

python学习笔记(持续更新)_第63张图片

 

#高通

img = cv2.imread('lena.jpg',0)

img_float32 = np.float32(img)

dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

rows, cols = img.shape
crow, ccol = int(rows/2) , int(cols/2)     # 中心位置

# 高通滤波 (去掉中间)
mask = np.ones((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 0

# IDFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Result'), plt.xticks([]), plt.yticks([])

plt.show()    

python学习笔记(持续更新)_第64张图片

 

总结:高频细节,低频边界。

 

 

 

 

 

 

 

 

 

 

 

问题:plt中imread参数问题;[::]的问题;BORDER_WRAP;ret,dst;元组问题;#img.ravel是多维矩阵变一维数组;

hist的参数; 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1])) #需要这样转化一下才可以得到图像形式的表达,分别对两个通道进行转换,通过*20得到0到255之间的数值;

 

你可能感兴趣的:(机器学习,python)