opencv基础

图像基本操作

数据读取-图像

cv2.IMREAD_COLOR:彩色图像

cv2.IMREAD_GRAYSCALE:灰色图像

import  cv2   //opencv的默认读取的格式是BGR

import matplotlib.pyplot  as  plt   //plt的默认读取格式是RGB

import numpy as np

img = cv2.imread('car.jpg',cv2.IMREAD_COLOR)  //读取图片,将图片转化成矩阵,就可以用numpy的操作

补充:imread函数中第二个参数的三种形式

 展示图片:

def  cv_show(name,img):

        cv2.imshow(name,img)

        cv2.waitKey(0)  //等待时间,毫米级,0表示任意键终止、

        cv2.destroyAllWindows()  //关闭窗口

保存图片:

cv2.imwrite(name,img)

数据读取-视频

cv2.Videocapture可以捕获摄像头,用数字来控制不同的设备,例如0,1

如果是视频文件的话,直接指定路径即可。

vc = cv2.VideoCapture('文件路径')

#检查是否打开正确

if  vc.isOpend():

        open,frame = vc.read()

else:

        open = False

# 对于视频的基本处理
while open:
    ret, frame = vc.read()
    if frame is None:
        break
    if ret == True:
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #转换成灰度图
        cv2.imshow('result',gray)
        if cv2.waitKey(10) & 0xFF ==27 :
            break
vc.release()
cv2.destroyAllWindows()

截取部分图像数据

img = cv2.imread('cat.jpg')
cat = img[0:200,0:200]
cv2.imshow('cat',cat)
cv2.waitKey(0)

颜色通道提取

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

b

b.shape  // b,g,r的具体内容不一样,但是shape是一样的

//合并

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

//只保留吗,某一通道的话,直接将另外两个通道赋值为0即可。

边界填充

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,cv2.BORDER_REFLECT)#反射法,对感兴趣的图像中的像素在两边进行复制,例如gfedcba|abcdefgh|hgfedcb 

reflect101 = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT_101)#反射法,就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba

wrap = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_WRAP)#外包装法  cdefgh|abcdefgh|abcdefg

constant = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_CONSTANT,value=0)#常量法,常数值填充。

图像数值计算(基于灰度图)

(1)直接相加

(img1+img2) //对应位置元素相加,加完之后对255取余

(2)add函数

cv2.add(img1,img2) // 对应位置元素相加,加完之后大于255的直接赋值255

图像融合

图像融合必须得一样的shape

(1)若shape不同,则首先通过cv2.resize(img,(XX,XX))进行调整

也可以cv2.resize(img,(0,0),fx=1,fy=3) 代表y轴的长度是x轴长度的三倍

(2)融合:cv2.addWeighted(img1,0.4,img2,0.6,0)

计算公式:0.4*img1+0.6*img2+0

图像阈值

ret,dst = cv2.threshold(src,thresh,maxval,type)

#src:输入图,只能输入单通道图像,通常来说是灰度图

#dst:输出图

#thresh:阈值

#maxval:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值

#type包含以下五种:

cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0(暗的为0,白的为255)

cv2.THRESH_BINARY_INV 上面那个的反转

cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变

cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0

cv2.THRESH_TOZERO_INV 上面那个的反转

图像平滑

        当图像有一些噪音点的时候,可以使用滤波消除噪音。

#均值滤波,其实就是简单的平均卷积操作

blur = cv2.blur(img,(3,3))

#方框滤波,基本和均值一样,可以选择归一化,如果不设置成true,会发生越界的问题

box = cv2.boxFilter(img,-1,(3,3),normalize=True)

#高斯滤波,高斯模糊的卷积核里面的数值是满足高斯分布的,相当于更加重视中间值,自己构建一个权重矩阵

aussion  = cv2.GaussionBlur(img,(5,5),1)

#中值滤波,相当于用中值代替(中位数)

meidan = cv2.medianBlur(img,5)

形态学--腐蚀操作

        腐蚀操作代表的就是去掉影响图像的多余部分,就像衣服起球,去掉球一样

erosion = cv2.erode(img,kernel,iterations=1)

#其中img是要修改的图像,kernel根据情况,对于灰度图来说要么是全0,要么是全1,iterations表示要循环的次数。

经过腐蚀后,图像整体会变小,但是多余的部分会除去。

形态学--膨胀操作

dia = cv2.dilate(img,kernel,iterations=1)

开运算,闭运算,梯度运算,礼帽,黑帽

开运算:先腐蚀再膨胀

闭运算:先膨胀再腐蚀

梯度运算:膨胀-腐蚀

礼帽运算:原始输入-开运算

黑帽运算:闭运算-原始输入

#开运算

opening  =  cv2.morphologyEx(image,cv2.MORPH_OPEN,kernel)

#闭运算

closing = cv2.morphologyEx(image,cv2.MORPH_CLOSE,kernel)

#梯度运算

gradient  = cv2.morphologyEx(image,cv2.MORPH_GRADIENT,kernel)

#礼帽运算

topht = cv2.morphologyEx(image,cv2.MORPH_TOPHT,kernel)

#黑帽运算

blackhat = cv2.morphologyEx(image,cv2.MORPH_BLACKHAT,kernel)

图像梯度-Sobel算子

Sobel算子就是计算图像梯度,由于只有不同区域才会有梯度,所以sobel算子类似于边缘检测。

下面是计算水平方向梯度和竖直方向梯度:

dst = cv2.Sobel(image,ddepth,dx,dy,ksize)

#ddepth代表图像的深度,一般是CV_64F 代表64位浮点数,因为计算出来不一定是正数。

补充一下,cv中的数据类型

#dx,dy代表的是水平方向和竖直方向,若选择水平方向,则dx为1,dy为0

#ksize代表的是sobel算子的大小。

#由于得到的不一定是正数,所以最后要求一个绝对值。

cv2.convertScaleAbs(a)

在使用Sobel算子计算梯度的过程中,一般先算水平x,再算y,最后求和。如果直接一下子把x和y全部计算出来的话,效果可能会不好。

图像梯度--Scharr算子和laplacian(拉普拉斯)算子

Scharr算子:其实和Sobel算子差不多,只是数值不同,得到的结果更明显

cv2.Scharr(img,CV_64F,dx,dy)

 laplacian(拉普拉斯)算子:应用到了二阶导

cv2. Laplacian(img,CV_64F)

#在这就没有x和y方向的选择了

Canny边缘检测

Canny边缘检测算法步骤:

1、高斯滤波器

 H代表的是高斯核,并且进行了归一化处理。

2、在计算梯度和方向的时候,用的是Sobel算子。下面给出梯度的计算公式和Sobel的算子核。

 3、非极大值抑制

法一:

 在上图中,g1,g2是像素点,我们可以计算它们的梯度值,但是dTmp1和dTmp2不是像素点,是亚像素点,没法直接计算它的梯度值,所以通过线性插值法计算。

法二:

 上图就是简化操作,相当于将亚像素点和最近的两个像素点进行比较,看更接近哪一个。

4、双阈值检测

        因为我们是边缘检测,所以梯度越大,成为边界的可能性越高。上图所示,A点大于maxVal,所以就是边界。再看C,C是大于minVal小于maxVal,且C连接A,所以保留C。而B是大于minVal小于maxVal但是B不连接A,所以舍弃。

 v1 = cv2.Canny(image,50,100)    

#50是minVal,100表示maxVal。

图像金字塔

 类似于普通的卷积操作

高斯金字塔:

(1)下采样方法(缩小)

(2)上采样方法(放大)

 将图像在每个方向扩大为原来的两倍,新增的行列以0填充。

使用先前同样的内核(乘以4)与放大后的图像卷积,获得近似值。

#上采样

up = cv2.pyrUp(img)

#下采样

down = cv2.pyrDown(img)  

拉普拉斯金字塔

公式含义:用原始图像-(对原始图像先进行Down在进行Up得到的结果),一直进行下去

 轮廓检测方法

cv2.findContours(img,mode.method)

mode:轮廓检索模式

        (1)RETR_EXTERNAL:只检索最外面的轮廓

        (2)RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中

        (3)RETR_CCOMP:检索所有的轮廓,并将他们组织为两层;顶层是各部分的外部边界,第二层是空洞的边界

        (4)RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;(直接用这个)

method:轮廓逼近方法

        (1)CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)

        (2)CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是函数只保留他们的终点部分。

具体轮廓检测:

#轮廓检测
contours = cv2.findContours(img,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)

#绘制轮廓
draw_img = img.copy()
res = cv2.drawContours(draw_img,contours,-1,(0,0,255),2)
#draw_img表示在draw_img上绘制。contours表示轮廓是什么。-1表示把轮廓全部画出来,其他数字不超过范围也可以。(0,0,255)是BGR的格式,2代表的线条的宽度。

 轮廓特征:

cnt = contours[0]  #单独拿出一个轮廓来计算

#计算面积

cv2.contourArea(cnt)


#计算周长,True表示闭合
cv2.arcLength(cnt,True)

轮廓近似:

epsilon = 0.1*cv2.arcLength(cnt,True)   //设定一个值,用来进行近似比较

approx = cv2.approxPolyDP(cnt,epsilon,True) //近似函数

边界的外接矩形:

x,y,w,h = cv2.boundingRect(cnt)   //得到的是外接矩形的四个顶点

img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

边界的外接圆:

(x,y),radius = cv2.minEnclosingCircle(cnt)

center = (int(x),int(y))        //中心坐标

radius = int(radius)     //半径

img = cv2.circle(img,center,radius,(0,255,0),2)

模板匹配

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

(A-a+1)*(B-b+1)

TM_SQDIFF:计算平方不同,计算出来的值越小,越相关

TM_CCORR:计算相关性,计算出来的值越大,越相关

TM_CCOEFF:计算相关系数,计算出来的值越大,越相关

TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关

TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关

TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关

res = cv2.matchTemplate(img1,img2,1)
#1代表哪种算法可以自己定义,1可以换成cv2.TM_SQDIFF
min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res)
#我们得到最大值最小值的坐标之后,根据模板的大小,即可在原图像上确定范围

补充eval()函数,用来执行一个字符串表达式,并返回表达式的值。

直方图

使用直方图统计各个像素值的个数

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

#images:原图像的格式为unit8或float32。当传入函数时应用中括号[],例如[img]

#channels:同样用中括号来括它。如果输入图像是灰度图,则[0].如果是彩色图像[0][1][2]代表BGR

#mask:掩盖图像。整幅图像为直方图则它为None,如果想把某一部分弄成直方图,就用mask

#histSize:BIN的数目,应该用[]括起来

#ranges:像素值的范围常为[0,256]

mask操作

mask = np.zeros(img.shape[:2],np.uint8)

mask[100:300,100:400] = 255

mask_img = cv2.bitwise_and(img,img,mask=mask)

均衡化

equ = cv2.equalizeHist(img)    #均衡化函数

plt.hist(equ.ravel(),256)               #ravel()将数组维度拉成一维数组

plt.show()

#自适应直方图均衡化

clahe = cv2.createCLAHE(clipLimit = 2.0,tieGridSize = (8,8))

res_clahe = clahe.apply(img)

傅里叶变换

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

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

低通滤波器:只保留低频,会使得图像模糊

高通滤波器:只保留高频,会使得图像细节增强

opencv中主要是cv2.dft()和cv2.idft(),输入图像需要先转换np.float32格式

得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现

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

#查看滤波
import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('cat.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

plt.subplot(121),plt.imshow(img,cmap='gray')
plt.title('Input'),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum,cmap='gray')
plt.title('Magnitude Spectrum'),plt.xticks([]),plt.yticks([])
plt.show()
#高通滤波器处理之后的图片
import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('cat.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,cool = int(rows/2),int(cols/2)
mask = np.ones((rows,cols,2),np.uint8)
mask[crow-30:crow+30,cool-30:cool+30] = 0

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'),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(img_back,cmap='gray')
plt.title('Magnitude Spectrum'),plt.xticks([]),plt.yticks([])
plt.show()

      结果:

图像特征-harris角点检测

cv2.cornerHarris()

(1)img:数据类型为float32的入图像,需要将图像  np.float32(img)

(2)blockSize:角点检测中指定区域的大小

(3)ksize:Sobel求导中使用的窗口大小

(4)k:取值参数为[0.04,0.06]

在展示图片之前:

img[dst>0.01*dsf.max()] = [0,0,255]   #设置一个值,来展示角点

scale invariant feature transform(SIFT):尺度不变特征变换

图像的尺度空间:在一定的范围里面,人眼可以轻松的判别出物体的大小,但是计算机却不行,为了让计算机对物体在不同的尺度下都有统一的认知,需要考虑图像在不同的尺度下都存在的特点。

尺度空间的获取通常是通过高斯模糊来实现。区分模糊,远近等。

通过不同的σ的高斯函数决定了图像的平滑程度,σ越大,图像越模糊。

多分辨率金字塔

 在金字塔的每层都要做多个高斯滤波。

高斯差分金字塔(DOG)

 呈现出来的不同的地方,通过差分之后的极值大的更重要,是特征。

DOG的计算公式:

DoG空间极值检测:

为了寻找极值点,每个像素点要和其图像域(同一尺度空间)和尺度域(相邻的尺度空间)的所有相邻节点进行比较,当大于(或小于)相邻节点的时候,这个点就是极值点。

关键点的精准定位:

拟合:把平面上一系列的点,用一条光滑的曲线连接起来。

检测出来的极值都是一些离散点,对这些离散点进行拟合,再求极值点,从而实现精准定位。

消除边界相应:

在高斯模糊之后,得到两个λ,一个是λmax,一个是λmin,当λmax/λmin>10的时候,我们就认为是边界,就可以将特征点删除。

特征点的主方向:

每个特征点都会得到位置,尺度,方向三个方向。具有多个方向的关键点会被复制成多份,然后将方向值分别赋予复制之后的特征点,这样一个特征点就产生了多个坐标、尺度相同,但是方向不同的特征点。

生成特征描述:

在对特征点进行梯度计算之后,使用直方图统计领域内像素的梯度和方向。

为了保持特征矢量的旋转不变性,要以特征点为中心,在附近的邻域内将坐标轴旋转θ度(计算公式在上面),即将坐标轴旋转为特征点的主方向。

以旋转之后的主方向为中心取8*8的窗口,球每个像素的梯度幅值和方向,箭头方向代表梯度方向,长度代表梯度幅值,然后利用高斯窗口对其进行加权运算,最后在每个4*4的小块上绘制8个方向的梯度直方图,计算每个梯度方向的累加值,即可形成一个种子点(将8*8的区域,分成4个4*4的区域,每一个区域是一个种子点),即每个特征的由4个种子点组成,每个种子点有8个方向的向量信息。

import cv2
import numpy as np

img = cv2.imread('cat.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#得到特征点
sift = cv2.xfeatures2d.SIFT_create() 
kp,des=sift.detectAndCompute(gray, None)


img = cv2.drawKeypoints(gray,kp,img)

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

特征匹配

特征匹配是看图像之间的特征点的相似性。

Brute-Force蛮力匹配算法

bf = BFMatcher(crossCheck=True)

#crossCheck代表的是两个特征点要相互匹配,例如A中的第i个特征点与B中的第j个特征点最近,并且B中的第j个特征点到A中的第i个特征点也是

#NORM_L2:归一化数组的(欧几里得距离),如果其他特征计算方法需要考虑不同的匹配计算方式


一对一的匹配:

match = bf.match(des1,des2)

match = sorted(match,key=lambda x:x.distance) #按接近程度排序一下

img3 = cv2.drawMatches(img1,kp1,img2,kp2,match[:10],None,flags=2)

k对最佳匹配

match = bf.match(des1,des2,k=2)

good = []

for m,n in match:

        if m.distance <0.75*n.distance:

                good.append([m])

img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)

随机抽样一致算法(RANSAC)

矩阵的自由度经常设置为8,也就是有8个未知数,最少需要4对特征点。 

这个算法的作用是当我们在进行图像拼接的时候,为了减少取到一些错误的特征点所带来的影响,就可以使用RANSAC算法。

背景建模

帧差法

由于场景中的目标在运动,目标的影响在不同影响帧中的位置不同。该类算法对时间上连续两帧图像进行差分运算,不同帧对应的像素点相减,判断灰度差的绝对值,当绝对值超过一定的阈值时,则判定为运动目标,从而实现目标的检测功能。

虽然帧差法简单,但是会引入噪音和空洞问题。

混合高斯模型

在进行前景检测前,先对背景进行训练,对图像中每个背景采用一个混合高斯模型进行模拟,每个背景的混合高斯的个数可以自适应。然后在测试阶段,对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为时背景,否则认为是前景。由于整个过程GMM模型在不断更新学习中,多以对动态背景有一定的鲁棒性。最后通过对一个有树枝摇摆的动态背景进行前景检测。在视频中对于像素点的变化情况应当是符合高斯分布。背景的实际分布应当是多个高斯分布混合在一起,每个高斯模型也可以带有权重。

混合高斯模型的学习方法:

1、首先初始化每个高斯模型的矩阵参数

2、去视频中T帧数据图像来训练高斯混合模型。来了第一个像素之后用他来当做第一个高斯分布

3、当后面来的像素值值,与前面已有的高斯分布的均值比较,如果该像素点的值与其模型均值差在3倍的方差内,则属于该分布,并对其参数进行更新

4、如果下一个来的像素不满足当前的高斯分布,用它来创建一个新的高斯分布

测试方法:

在测试阶段,对新来的像素点的值与混合高斯模型中的每一个均值进行比较,如果其差值在2倍的方差之间的话,则认为是背景,否则认为是前景。将前景赋值为255,背景赋值为0。这样就形成了一副前景二值图。

import cv2
import numpy as np

cap = cv2.VideoCapture("back.avi")

#形态学操作需要使用
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
#创建混合高斯模型用于背景建模
fgbg = cv2.createBackgroundSubtractorMOG2()

while(True):
    ret, frame = cap.read()
    fgmask = fgbg.apply(frame)

    #形态学开运算去早点
    fgmask = cv2.morphologyEx(fgmask,cv2.MORPH_OPEN,kernel)

    #寻找视频中的轮廓
    im, contours, hierarchy = cv2.findContours(fgmask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

    for c in contours:
        #计算各轮廓的周长
        perimeter = cv2.arcLength(c,True)
        if perimeter > 188 :
            #找到一个直矩形
            x,y,w,h = cv2.boundingRect(c)
            #画出这个矩形
            cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
    cv2.imshow('frame',frame)
    cv2.imshow('fgmask',fgmask)
    k = cv2.waitKey(150) & 0xff
    if k == 27:
        break
cap.release()
cv2.destroyAllWindows()

光流估计

光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标检测。

亮度恒定:同一点随着时间的变化,其亮度不会发生改变

小运动:随着时间的变换不会引起位置上的剧烈变动,只有小运动情况下才能使用前后帧之间单位位置变换引起的灰度变化去近似灰度对位置的偏导数

空间一致:一个场景上邻近的点投影到图像上也是临近点,且邻近点速度一直。因为光流法基本方程约束只有一个,而要求x,y反向的速度,有两个未知变量。所以需要连立n多个方程求解。

Lucas-Kanade算法

约束方程:(根据三个性质)

 光流估计函数:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值