OpenCV学习笔记(持续更新)

Opencv的绘制函数

线段绘制

import cv2 as cv
import numpy as np


# cv2.line(img,pts,color,thickness.linetype)
# 参数说明
# img待绘制图像(底板)
# pts起点和终点
# color颜色
# thickness线条粗细(-1为填充,默认1)
# linetype线形,默认8型
def draw_line():
    img = np.zeros((512, 512, 3), np.uint8)
    cv.line(img, (0, 0), (500, 500), (255, 0, 0), 5)
    return img


if __name__ == "__main__":
    win = 'test'
    cv.namedWindow(win)
    cv.imshow(win,draw_line())
    cv.waitKey(0)
    cv.destroyWindow(win)

绘制矩形

# cv2.rectangle(img,pts,color,thickness.linetype)
def draw_rectangle():
    img = np.zeros((512, 512, 3), np.uint8)
    cv.rectangle(img, (100, 100), (200, 200), (255, 0, 0), 1)
    return img

绘制椭圆

# cv.ellipse(img,椭圆中心,X/Y轴长度,旋转角,起始角度,结束角度)
def draw_ellipse():
    img = np.zeros((512, 512, 3), np.uint8)
    cv.ellipse(img, (256, 256), (200, 200), 0, 0, 180, (255, 0, 0), -1)
    return img

绘制多边形

# cv.polylines(img,pts,isClosed,color,thickness,linetype)
def draw_polylines():
    img = np.zeros((512, 512, 3), np.uint8)
    pts = np.array([[10, 10], [100, 100], [10, 300], [100, 300]])
    pts = pts.reshape((-1, 1, 2))
    cv.polylines(img, [pts], True, (255, 255, 255))
    return img

添加文字

# cv.putText(img,文字,起点,字体,缩放比,颜色,线宽,线形)
# 不能显示中文字符
def draw_text():
    img = np.zeros((512, 512, 3), np.uint8)
    font = cv.FONT_HERSHEY_SIMPLEX
    cv.putText(img, '醉后不知天再水,满船清梦压星河', (0, 256), font, 4, (255, 255, 255), 2)
    return img

图像几何变换

# 仿射变换函数cv.warpAffine(src,M,dsize,flags,borderMode,borderValue)
# 参数(输入图像,变换矩阵,输入图像的大小,插值方式的组合(int),边界像素模式(int)
#       边界像素填充值【默认0】)
# M作为仿射变换矩阵,一般反应平移或旋转关系,为InputArray类型的2X3的变换矩阵
# flags表示插值方式,默认线性插值,具体包括以下
# INTER_LINEAR线性插值
# INTER_NEAREST最近邻插值
# INTER_ATEA区域插值
# INTER_CUBIC三次样条插值
# INTER_LANCZOS4 Lacczos插值

平移

# 图像平移:将图像中所有的点按照指定的平移量水平或者竖直平移
# 变换公式:x+=T(x);y+=T(y)
def Image_translation(img_path):
    img = cv.imread(img_path)
    # 构造平移矩阵H:(T(X),T(y))
    # H = np.float32([[1, 0, 50], [0, 1, 25]])在x方向移动50,在y方向移动25
    H = np.float32([[1, 0, 50], [0, 1, 25]])
    rows, cols = img.shape[:2]
    res = cv.warpAffine(img, H, (cols, rows))
    return res

缩放

# 图像缩放:图像大小按照指定的比例进行放大或者缩小
# 下采样(downsample):缩小图像
# 上采样(upsample):放大图像,提高分辨率
# cv.resize(src,dsize,fx,fy,interpolation)
# fx,fy表示沿轴方向的比例因子
def Image_Zoom(img_path):
    img = cv.imread(img_path)
    h, w = img.shape[:2]
    res = cv.resize(img, (int(0.8*w),int(0.8*h)),interpolation=cv.INTER_AREA)
    return res  

旋转

# 图像旋转:以图像中心为原点,旋转一定角度
# (1)旋转前,为了避免信息丢失,一定要有坐标平移
# (2)旋转后,对于产生的空洞必须进行填充,即常规插值处理
# cv.getRotationMatrix2D(center,angle.scale)
def Image_rotate(img_path):
    img = cv.imread(img_path)
    h, w = img.shape[:2]
    M = cv.getRotationMatrix2D((w/2,h/2),45,-1)
    dst = cv.warpAffine(img,M,(w,h))
    return dst

仿射变换

# 仿射变换:平移、旋转、缩放、裁剪、反射(镜像)
# M = cv.getAffineTransform(pos1,pos2)
# pos1为变换前位置
# pos2为变换后位置
def Image_Affine(img_path):
    img = cv.imread(img_path)
    rows, cols = img.shape[:2]
    pos1 = np.float32([[50, 50], [200, 50], [50, 200]])
    pos2 = np.float32([[10, 100], [200, 50], [100, 250]])
    M = cv.getAffineTransform(pos1, pos2)
    res = cv.warpAffine(img, M, (cols, rows))
    return res  

透视变换

# 透视变换:图像投影到新的视平面
def Image_perspective(img_path):
    img = cv.imread(img_path)
    rows, cols = img.shape[:2]
    pos1 = np.float32([[114, 82], [287, 150], [50, 200],[40, 200]])
    pos2 = np.float32([[10, 100], [200, 50], [100, 250],[150, 250]])
    M = cv.getPerspectiveTransform(pos1, pos2)
    res = cv.warpPerspective(img, M, (cols, rows))
    return res

图像滤波

# 图像滤波
# 滤波实际上是信号处理的一个概念,图像可以看成一个二维信号,其中像素点的灰度值代表信号强弱
# 高频:图像上剧烈变化的部分
# 低频:图像灰度值变化缓慢的部分
# 根据图像高低频,设置高通和低通滤波器。高通滤波器可以检测变化尖锐的部分,低通滤波可以让图像平滑,消除噪声
# 滤波作用:高通用于边缘检测,低通用于平滑去噪
# 线性滤波:方框/均值/高斯滤波
# 非线性滤波:中值/双边滤波
# 领域算子:利用给定像素周围的像素值决定此像素的最终输出值
# 线性滤波:像素输出取决于输入像素的加权和

方框滤波(Box Filter)

在这里插入图片描述
normalize = true 与均值滤波相同
normalize = false 很容易发生溢出

# box filter被封装在boxFilter函数,用于模糊图像
# cv2.boxFilter(src,depth,ksize,normalize)
# 参数说明:输入图像,目标图像通道数,kernel_size,是否取平均
def box_filter(img_path):
    img = cv.imread(img_path, cv.IMREAD_UNCHANGED)
    rows, cols = img.shape[:2]
    res = cv.boxFilter(img, -1, ksize=(3, 3), normalize=1)
    return res

均值滤波

# 均值滤波
# cv.blur(src,ksize)
def mean_filter(img_path):
    img = cv.imread(img_path, cv.IMREAD_UNCHANGED)
    rows, cols = img.shape[:2]
    res = cv.blur(img, ksize=(3, 3))
    return res

高斯滤波

在这里插入图片描述

# 高斯滤波
# 高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。高斯滤波的kernel权重并不相同,中间像素权重最高,离中心越远权重越小
# 实质上是基于二维高斯函数
# 高斯滤波相比均值滤波效率较慢,但是可以消除高斯噪声,能保留更多的图像细节,被公认最有用filter
# cv2.Gaussianblur(src, ksize, std)
# 参数:input,kernel,标准差
# std标准差,在平滑时,调整std实际上是在调整周围像素对当前像素的影响程度,调大即提高远处像素对中心像素影响,结果也会随之更加平滑
def Gaussian_filter(img_path):
    img = cv.imread(img_path, cv.IMREAD_UNCHANGED)
    rows, cols = img.shape[:2]
    res = cv.GaussianBlur(img, ksize=(3, 3), sigmaX=0)
    return res

中值滤波

# 中值滤波是一种非线性滤波
# 使用像素点邻域灰度值的中值代替中心像素灰度值,中值滤波可以去除椒盐噪声和斑点噪声
# cv2.medianBlur(img,ksize)
def median_filter(img_path):
    img = cv.imread(img_path, cv.IMREAD_UNCHANGED)
    rows, cols = img.shape[:2]
    res = cv.medianBlur(img, ksize=3)
    return res

双边滤波

# 双边滤波
# 双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空间与信息和灰度相似性,达到保边去噪的目的,具有简单、非迭代、局部处理的特点。
# cv2.bilateralFilter(src=image, d, sigmaColor, sigmaSpace)
# 参数:input,图像的输入直径,图像灰度相似性高斯函数标准差,空间高斯标准差
def bilateral_filter(img_path):
    img = cv.imread(img_path, cv.IMREAD_UNCHANGED)
    rows, cols = img.shape[:2]
    res = cv.bilateralFilter(img, -1, 15, 10)
    return res

图像增强

直方图均衡化

在这里插入图片描述

# 直方图均衡化
# 直方图均衡化是将原图像通过某种变换,得到一幅灰度直方图为均匀分布的新图像的方法。
# 直方图均衡化方法的基本思想是对在图像中像素个数多的灰度级进行展宽,而对像素个数少的灰度级进行缩减。从而达到清晰图像的目的。
# cv2.equalizeHist(img)
# 步骤
# 统计直方图中每个灰度级出现的次数;
# 计算累计归一化直方图;
# 重新计算像素点的像素值
def equalizeHist_filter(img_path):
    img = cv.imread(img_path, 1)
    (b, r, g) = cv.split(img)
    bh = cv.equalizeHist(b)
    gh = cv.equalizeHist(g)
    rh = cv.equalizeHist(r)
    res = cv.merge((bh, gh, rh))
    rows, cols = img.shape[:2]
    return res

Gamma变换

在这里插入图片描述

# Gamma变换
# Gamma变换是对输入图像灰度值进行的非线性操作, 使输出图像灰度值与输入图像灰度值呈指数关系
# Gamma变换就是用来图像增强,其提升了暗部细节, 通过非线性变换,让图像从暴光强度的线性响应变得  更接近人眼感受的响应,即将漂白(相机曝光)或过 暗(曝光不足)的图片,进行矫正
def gamma(img_path, gamma=1.0):
    img = cv.imread(img_path)
    invgamma = 1.0 / gamma
    table = []
    for i in range(256):
        table.append(((i / 255.0) * invgamma) * 255)
    table = np.array(table).astype('uint8')
    return cv.LUT(img, table)

在这里插入图片描述

形态学操作

# 形态学,是图像处理中应用最为广泛的技术之一,主要用于从图像中提取 对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目 标对象最为本质的形状特征,如边界和连通区域等
# 结构元素:设有两幅图像B,X。若X是被处理的对象,而B是用来处理X的,则称B为结构元素(structure element),又被形象地称做刷子
# 结构元素通常都是一些比较小的图像。
# 图像的膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学 运算,其中膨胀类似于 领域扩张 ,将图像中的白色部分进行 扩张,其运行结果图比原图的白色区域更大;腐蚀类似于 领域 被蚕食 ,将图像中白色部分进行缩减细化,其运行结果图比原 图的白色区域更小

腐蚀(erode)

在这里插入图片描述

# 腐蚀‘-’
# 函数:cv2.erode(src,element,anchor,iterations)
# 参数:input,kernel.kernel_center,腐蚀次数
def erode_operation(img_path):
    img = cv.imread(img_path, 1)
    res = cv.erode(img, (3, 3))
    return res

膨胀(dilation)

在这里插入图片描述

# 膨胀‘+’
# 腐蚀的对偶运算
def dilate_operation(img_path):
    img = cv.imread(img_path, 1)
    res = cv.dilate(img, (3, 3))
    return res

开运算

在这里插入图片描述

"""
cv2.morphologyEx(src,      # 输入图片
                 op,       # 需要处理类型的函数:(cv2.MORPH_OPEN,cv2.MORPH_CLOSE,cv2.MORPH_GRADIENT)
                 kernel,   # 卷积核大小
                 dst=None, 
                 anchor=None, 
                 iterations=None,     #迭代次数,默认1次
                 borderType=None, 
                 borderValue=None)
"""


# 开运算
# 腐蚀+膨胀
# 开运算总结:
# 1. 开运算能够除去孤立的小点,毛刺和小桥,而总的位置和形状不变。
# 2. 开运算是一个基于几何运算的滤波器。
# 3. 结构元素大小的不同将导致滤波效果的不同。
# 4. 不同的结构元素的选择导致了不同的分割,即提取出不同的特征。
def open_operation(img_path):
    img = cv.imread(img_path, 1)
    res = cv.morphologyEx(img, op=cv.MORPH_OPEN, kernel=(3,3))
    return res

闭运算

在这里插入图片描述

# 闭运算
# 膨胀+腐蚀
# 闭运算总结:
# 1. 闭运算能够填平小湖(即小孔),弥合小裂缝,而总的位置和形状不变。
# 2. 闭运算是通过填充图像的凹角来滤波图像的。
# 3. 结构元素大小的不同将导致滤波效果的不同。
# 4. 不同结构元素的选择导致了不同的分割。
def close_operation(img_path):
    img = cv.imread(img_path, 1)
    res = cv.morphologyEx(img, op=cv.MORPH_CLOSE, kernel=(3, 3))
    return res

形态学梯度(grad)

# 形态学梯度
# 基础梯度:基础梯度是用膨胀后的图像减去腐蚀后的图像得到差值图像。
# 内部梯度:是用原图像减去腐蚀之后的图像得到差值图像。
# 外部梯度:图像膨胀之后再减去原来的图像得到的差值图像。
def grad_operation(img_path):
    img = cv.imread(img_path, 1)
    eroder_img = cv.erode(img, (3, 3))
    dilate_img = cv.dilate(img, (3, 3))
    or_gard = dilate_img - eroder_img
    in_grad = img - eroder_img
    out_grad = dilate_img - img
    return or_gard, in_grad, out_grad

顶帽和黑帽

# Top Hat:原图像和开运算差值,突出原图像中周围亮的区域
# Block Hat:闭运算和原图像差值,突出原图像中比周围暗的区域
def Hat_operation(img_path):
    img = cv.imread(img_path, 1)
    open_img = cv.morphologyEx(img, op=cv.MORPH_OPEN, kernel=(3, 3))
    close_img = cv.morphologyEx(img, op=cv.MORPH_CLOSE, kernel=(3, 3))
    Top_hat = img - open_img
    Black_hat = close_img - img
    return Top_hat, Black_hat

固定阈值分割

# 图像分割是指将图像分成若干具有相似性质的区域的过程,主要有基于阈值、基于区域、基于边缘、聚类、基于图论和基于深度学习的图像分割方法等。
# 图像分割分为语义分割和实例分割。
# 分割的原则就是使划分后的子图在内部保持相似度最大,而子图之间的相似度保持最小。

直方图双峰法

在这里插入图片描述

# 直方图双峰法
# 双峰法:六十年代中期提出的直方图双峰法 (也称mode法) 是典型的全局单阈值分割方法
# 基本思想:假设图像中有明显的目标和背景 ,则其灰度直方图呈双峰分布,当灰度级直 方图具有双峰特性时,选取两峰之间的谷对应的灰度级作为阈值
# 函数:cv2.threshold(src, thresh, maxval, type)
# 参数:原图、阈值、当像素值高于(小于)阈值时,应该被赋予的新的像素值、阈值方法。、
# 返回值:函数有两个返回值,一个为retVal, 一个阈值化处理之后的图像。
def mode_operation(img_path):
    img = cv.imread(img_path, 1)
    ret, th = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
    return th

在这里插入图片描述
在这里插入图片描述

自动阈值分割

均值自适应和高斯加权自适应

# 函数:cv2.adaptiveThreshold(img,max_threshold,小区域阈值计算方式,五种阈值方式,小区域的面积,最终阈值等于小区域计算出的阈值再减去此值)
# 特定:自适应阈值会每次取图片的一小部分计算阈值,这样图片不同区域的阈值就不尽相同,适用于明暗分布不均的图片。
def adaptivethr_operation(img_path):
    img = cv.imread(img_path, 0)
    th = cv.adaptiveThreshold(
        img,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,11,4,
    )
    return th

在这里插入图片描述

迭代法阈值分割

# 迭代阈值分割步骤
# 1. 求出图像的最大灰度值和最小灰度值,分别记为ZMAX和ZMIN,令初始阈值T0=(ZMAX+ZMIN)/2;
# 2. 根据阈值TK将图象分割为前景和背景,分别求出两者的平均灰度值ZO和ZB ;
# 3. 求出新阈值TK+1=(ZO+ZB)/2;
# 4. 若TK==TK+1,则所得即为阈值;否则转2,迭代计算;
# 5 . 使用计算后的阈值进行固定阈值分割。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm


def best_thresh(img):
    img_array = np.array(img).astype(np.float32)  # 转化成数组
    I = img_array
    zmax = np.max(I)
    zmin = np.min(I)
    tk = (zmax + zmin) / 2  # 设置初始阈值
    # 根据阈值将图像进行分割为前景和背景,分别求出两者的平均灰度zo和zb
    b = 1
    m, n = I.shape;
    while b == 0:
        ifg = 0
        ibg = 0
        fnum = 0
        bnum = 0
        for i in range(1, m):
            for j in range(1, n):
                tmp = I(i, j)
                if tmp >= tk:
                    ifg = ifg + 1
                    fnum = fnum + int(tmp)  # 前景像素的个数以及像素值的总和
                else:
                    ibg = ibg + 1
                    bnum = bnum + int(tmp)  # 背景像素的个数以及像素值的总和
        # 计算前景和背景的平均值
        zo = int(fnum / ifg)
        zb = int(bnum / ibg)
        if tk == int((zo + zb) / 2):
            b = 0
        else:
            tk = int((zo + zb) / 2)
    return tk


img = cv2.imread(r'C:\Users\Lenovo\Desktop\2073.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img = cv2.resize(gray, (200, 200))  # 大小
yvzhi = best_thresh(img)
ret1, th1 = cv2.threshold(img, yvzhi, 255, cv2.THRESH_BINARY)
print(ret1)
plt.imshow(th1, cmap=cm.gray)
plt.show()

Otsu大津法

在这里插入图片描述

# 大津法(最大类间方差法),1979年日本学者大津提出,是一种基于全局阈值的自适应方法。
# 灰度特性:图像分为前景和背景。当取最佳阈值时,两部分之间的差别应该是最大的,衡量差别的标准为最大类间方差。
# 直方图有两个峰值的图像,大津法求得的T近似等于两个峰值之间的低谷。

在这里插入图片描述符号说明:
T:图像I(x,y)前景和背景的分割阈值;
ω1:属于前景的像素点数占整幅图像的比例记,其平均灰度μ1;
ω2:背景像素点数占整幅图像的比例为,其平均灰度为μ2;
μ:图像的总平均灰度;
g:类间方差;
N1:设图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数;
N2:像素灰度大于阈值T的像素个数。

def Otsu_operation(img_path):
    img = cv.imread(img_path, 0)
    img = cv.GaussianBlur(img, (5, 5), 0)
    res, th = cv.threshold(img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
    return th

边缘检测算子

梯度:梯度是一个向量,梯度方向指向函数变化最快的方向,大小就是他的模,也就是最大的变化率,对于二元函数而言,他在(x,y)的梯度就是grad(x,y)
在这里插入图片描述

梯度向量的幅度和方向角
在这里插入图片描述
图像梯度:图像梯度即图像中灰度变化的度量,求图像梯度的过程是在二维离散图像上灰度级变化很快的点的集合
在这里插入图片描述
图像在点(x,y)的梯度为
在这里插入图片描述
要理解梯度图的生成,就要先了解模板卷积的过程,模板卷积是模板运算的一种方式,其步骤如下:
(1)将模板在输入图像中漫游,并将模板中心与图像中某个像素位置重合;
(2)将模板上各个系数与模板下各对应像素的灰度相乘;
(3)将所有乘积相加(为保持灰度范围,常将结果再除以模板系数之和,后面梯度算子模板和为0的话就不需要除了);
(4)将上述运算结果(模板的响应输出)赋给输出图像中对应模板中心位置的像素。
在这里插入图片描述
梯度图的生成和模板卷积相同,不同的是要生成梯 度图,还需要在模板卷积完成后计算在点(x,y)梯度 的幅值,将幅值作为像素值,这样才算完
在这里插入图片描述
**注意:梯度图上每个像素点的灰度值就是梯度向量 的幅度 生成梯度图需要模板,上图为水平和竖直方向最简单的模板 **
在这里插入图片描述

梯度算子

梯度算子是一阶导数算子,是水平G(x)和竖直G(y)方向对应模板的组合,也 有对角线方向。
常见梯度算子包括:Roberts交叉算子,Prewitt算子,Sobel算子

Roberts交叉算子

在这里插入图片描述
在这里插入图片描述
优点:边缘定位较准,适用于边缘明显且噪声较少的图像。
缺点:
(1)没有描述水平和竖直方向的灰度变化,只关注了对角线方向,容易造成遗漏。
(2)鲁棒性差。由于点本身参加了梯度计算,不能有效的抑制噪声的干扰。

import cv2
import numpy as np
from scipy import signal


def roberts(I, _boundary='fill', _fillvalue=0):
    # 图像的高,宽
    H1, W1 = I.shape[0:2]

    # 卷积核的尺寸
    H2, W2 = 2, 2

    # 卷积核1 和 锚点的位置
    R1 = np.array([[1, 0], [0, -1]], np.float32)
    kr1, kc1 = 0, 0

    # 计算full卷积
    IconR1 = signal.convolve2d(I, R1, mode='full', boundary=_boundary, fillvalue=_fillvalue)
    IconR1 = IconR1[H2 - kr1 - 1:H1 + H2 - kr1 - 1, W2 - kc1 - 1:W1 + W2 - kc1 - 1]

    # 卷积核2 和 锚点的位置
    R2 = np.array([[0, 1], [-1, 0]], np.float32)
    kr2, kc2 = 0, 1
    # 再计算full卷积
    IconR2 = signal.convolve2d(I, R2, mode='full', boundary=_boundary, fillvalue=_fillvalue)
    IconR2 = IconR2[H2 - kr2 - 1:H1 + H2 - kr2 - 1, W2 - kc2 - 1:W1 + W2 - kc2 - 1]

    return (IconR1, IconR2)


if __name__ == '__main__':
    I = cv2.imread(r'C:\Users\Lenovo\Desktop\2073.jpg', cv2.IMREAD_GRAYSCALE)
    # 卷积,注意边界一般扩充采用的symm
    IconR1, IconR2 = roberts(I, 'symm')

    # 45度方向上的边缘强度的灰度级显示
    IconR1 = np.abs(IconR1)
    edge45 = IconR1.astype(np.uint8)

    # 135度方向上的边缘强度的灰度级显示
    IconR2 = np.abs(IconR2)
    edge135 = IconR2.astype(np.uint8)

    # 用平方和的开方来衡量最后输出的边缘
    edge = np.sqrt(np.power(IconR1, 2.0) + np.power(IconR2, 2.0))
    edge = np.round(edge)
    edge[edge > 255] = 255
    edge = edge.astype(np.uint8)

    # 显示边缘
    cv2.imshow('edge', edge)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Prewitt算子

在这里插入图片描述在这里插入图片描述

import cv2
import numpy as np
from scipy import signal


def prewitt(I, _boundary='symm', ):
    # prewitt算子是可分离的。 根据卷积运算的结合律,分两次小卷积核运算

    # 算子分为两部分,这是对第一部分操作
    # 1: 垂直方向上的均值平滑
    ones_y = np.array([[1], [1], [1]], np.float32)
    i_conv_pre_x = signal.convolve2d(I, ones_y, mode='same', boundary=_boundary)
    # 2: 水平方向上的差分
    diff_x = np.array([[1, 0, -1]], np.float32)
    i_conv_pre_x = signal.convolve2d(i_conv_pre_x, diff_x, mode='same', boundary=_boundary)

    # 算子分为两部分,这是对第二部分操作
    # 1: 水平方向上的均值平滑
    ones_x = np.array([[1, 1, 1]], np.float32)
    i_conv_pre_y = signal.convolve2d(I, ones_x, mode='same', boundary=_boundary)
    # 2: 垂直方向上的差分
    diff_y = np.array([[1], [0], [-1]], np.float32)
    i_conv_pre_y = signal.convolve2d(i_conv_pre_y, diff_y, mode='same', boundary=_boundary)

    return (i_conv_pre_x, i_conv_pre_y)


if __name__ == '__main__':
    I = cv2.imread(r'', cv2.IMREAD_GRAYSCALE)

    i_conv_pre_x, i_conv_pre_y = prewitt(I)

    # 取绝对值,分别得到水平方向和垂直方向的边缘强度
    abs_i_conv_pre_x = np.abs(i_conv_pre_x)
    abs_i_conv_pre_y = np.abs(i_conv_pre_y)

    # 水平方向和垂直方向上的边缘强度的灰度级显示
    edge_x = abs_i_conv_pre_x.copy()
    edge_y = abs_i_conv_pre_y.copy()

    # 将大于255的值截断为255
    edge_x[edge_x > 255] = 255
    edge_y[edge_y > 255] = 255

    # 数据类型转换
    edge_x = edge_x.astype(np.uint8)
    edge_y = edge_y.astype(np.uint8)

    # 利用abs_i_conv_pre_x 和 abs_i_conv_pre_y 求最终的边缘强度
    # 求边缘强度有多重方法, 这里使用的是插值法
    edge = 0.5 * abs_i_conv_pre_x + 0.5 * abs_i_conv_pre_y

    # 边缘强度灰度级显示
    edge[edge > 255] = 255
    edge = edge.astype(np.uint8)
    cv2.imshow('edge', edge)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Sobel算子

在这里插入图片描述
在这里插入图片描述

# Sobel
# cv2.Sobel(src, ddepth, dx, dy,ksize)
# 参数:需要处理的图像,图像的深度,-1表示采用的是与原图像相同的深度 目标图像的深度必须大于等于原图像的深度; 参数3,4:dx和dy表示的是求导的阶数,0表示这个方向上没有求导,ksize是Sobel算子的大小,必须为1、3、5、7。一般为0 1 2
def sobel(img_path):
    img = cv.imread(img_path, 0)
    sobel_img = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5)
    return sobel_img

Canny边缘检测法

# Canny
# Canny算子是先平滑后求导数的方法。John Canny研究了最优边缘检测方法所需的特性,给出了评价边缘检测性能优劣的三个指标:
# 1 好的信噪比,即将非边缘点判定为边缘点的概率要低,将边缘点判为非边缘点的概率要低;
# 2 高的定位性能,即检测出的边缘点要尽可能在实际边缘的中心;
# 3 对单一边缘仅有唯一响应,即单个边缘产生多个响应的概率要低,并且虚假响应边缘应该得到最大抑制。
# cv2.Canny(image, th1, th2,Size)
# 参数:原图像、阈值1、阈值2、kernel_size
# 步骤:
# 1. 彩色图像转换为灰度图像(以灰度图单通道图读入)
# 2. 对图像进行高斯模糊(去噪)
# 3. 计算图像梯度,根据梯度计算图像边缘幅值与角度
# 4. 沿梯度方向进行非极大值抑制(边缘细化)
# 5. 双阈值边缘连接处理
# 6. 二值化图像输出结果
def Canny(img_path):
    img = cv.imread(img_path, 0)
    rst = cv.Canny(img, 80, 150, (3, 3))
    return rst

连通区域分析法

连通区

连通区域(Connected Component)一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域,连通区域分析是指将图像中的各个连通区域找出并标记 连通区域分析是一种在CV和图像分析处理的众多应用领域中较为常用和基本的方法。例如:OCR识别中字符分割提取(车牌识别 文本识别 字幕识别等) 视觉跟踪中的运动前景 目标分割与提取(行人入侵检测 遗留物体检测 基于视觉的车辆检测与跟踪等) 医学图像处 理(感兴趣目标区域提取)等。在需要将前景目标提取出来以便后续进行处理的应用场景中都能够用到连通区域分析 方法,通常连通区域分析处理的对象是一张二值化后的图像。
在图像中,最小的单位是像素,每个像素周围有邻接像素,常见的邻接关系有2种:4邻接与8邻接。
在这里插入图片描述
如果A与B连通,B与C连通,则A与C连通,在视觉上看来,彼此连通的点形成了一个区域,而不连 通的点形成了不同的区域 这样的一个所有的点彼此连通点构成的集合,我们称为一个连通区域。

Two-Pass算法

第一次扫描
从左上角开始遍历像素点,找到第一个像素为255的点,label=1;
当该像素的左邻像素和上邻像素为无效值时,给该像素置一个新的label值,label ++,记录集合;
当该像素的左邻像素或者上邻像素有一个为有效值时,将有效值像素的label赋给该像素的label值;
当该像素的左邻像素和上邻像素都为有效值时,选取其中较小的label值赋给该像素的label值
第二次扫描
对每个点的label进行更新,更新为其对于其集合中最小的label
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

import cv2
import numpy as np

# 4邻域的连通域和 8邻域的连通域
# [row, col]
NEIGHBOR_HOODS_4 = True
OFFSETS_4 = [[0, -1], [-1, 0], [0, 0], [1, 0], [0, 1]]

NEIGHBOR_HOODS_8 = False
OFFSETS_8 = [[-1, -1], [0, -1], [1, -1],
             [-1, 0], [0, 0], [1, 0],
             [-1, 1], [0, 1], [1, 1]]


# 第二遍扫描
def reorganize(binary_img: np.array):
    index_map = []
    points = []
    index = -1
    rows, cols = binary_img.shape
    for row in range(rows):
        for col in range(cols):
            var = binary_img[row][col]
            if var < 0.5:
                continue
            if var in index_map:
                index = index_map.index(var)
                num = index + 1
            else:
                index = len(index_map)
                num = index + 1
                index_map.append(var)
                points.append([])
            binary_img[row][col] = num
            points[index].append([row, col])
    # print(binary_img)
    # print(points)
    return binary_img, points


# 四领域或八领域判断
def neighbor_value(binary_img: np.array, offsets, reverse=False):
    rows, cols = binary_img.shape
    label_idx = 0
    rows_ = [0, rows, 1] if reverse == False else [rows - 1, -1, -1]
    cols_ = [0, cols, 1] if reverse == False else [cols - 1, -1, -1]
    for row in range(rows_[0], rows_[1], rows_[2]):
        for col in range(cols_[0], cols_[1], cols_[2]):
            label = 256
            if binary_img[row][col] < 0.5:
                continue
            for offset in offsets:
                neighbor_row = min(max(0, row + offset[0]), rows - 1)
                neighbor_col = min(max(0, col + offset[1]), cols - 1)
                neighbor_val = binary_img[neighbor_row, neighbor_col]
                if neighbor_val < 0.5:
                    continue
                label = neighbor_val if neighbor_val < label else label
            if label == 255:
                label_idx += 1
                label = label_idx
            binary_img[row][col] = label
    print('第一遍扫描:', binary_img)
    print('开始第二遍...')
    return binary_img


# binary_img: bg-0, object-255; int
# 第一遍扫描
def Two_Pass(binary_img: np.array, neighbor_hoods):
    if neighbor_hoods == NEIGHBOR_HOODS_4:
        offsets = OFFSETS_4
    elif neighbor_hoods == NEIGHBOR_HOODS_8:
        offsets = OFFSETS_8
    else:
        raise ValueError

    binary_img = neighbor_value(binary_img, offsets, False)

    return binary_img


if __name__ == "__main__":
    # 创建四行七列的矩阵
    binary_img = np.zeros((4, 7), dtype=np.int16)
    # 指定点设置为255
    index = [[0, 2], [0, 5],
             [1, 0], [1, 1], [1, 2], [1, 4], [1, 5], [1, 6],
             [2, 2], [2, 5],
             [3, 1], [3, 2], [3, 4], [3, 5], [3, 6]]
    for i in index:
        binary_img[i[0], i[1]] = np.int16(255)

    print("原始二值图像")
    print(binary_img)

    # print("Two_Pass")
    # 调用Two Pass算法,计算第一遍扫面的结果
    binary_img = Two_Pass(binary_img, NEIGHBOR_HOODS_4)
    # print(binary_img)
    # 计算第一遍扫面的结果
    binary_img, points = reorganize(binary_img)
    print(binary_img)
    # print(points)

区域生长算法

区域生长是一种串行区域分割的图像分割方法。区域生长是指从某个像素出发,按照一定的准则,逐步加入邻近像素,当满足一定的条件时,区域生长终止。

基本思想:将具有相似性质的像素集合起来构成区域。
在这里插入图片描述

步骤:
(1)对图像顺序扫描,找到第1个还没有归属的像素, 设该像素为(x0, y0);
(2)以(x0, y0)为中心, 考虑(x0, y0)的4邻域像素(x, y)如果(x0, y0)满足生长准则, 将(x, y)与(x0, y0)合并(在同一区域内), 同时将(x, y)压入堆栈;
(3)从堆栈中取出一个像素, 把它当作(x0, y0)返回到步骤2;
(4)当堆栈为空时,返回到步骤1;
(5)重复步骤1 - 4直到图像中的每个点都有归属时;
(6)生长结束。
在这里插入图片描述

import cv2
import numpy as np


class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def getX(self):
        return self.x

    def getY(self):
        return self.y


connects = [Point(-1, -1), Point(0, -1), Point(1, -1), Point(1, 0),
            Point(1, 1), Point(0, 1), Point(-1, 1), Point(-1, 0)]


# 计算两个点间的欧式距离
def get_dist(seed_location1, seed_location2):
    l1 = im[seed_location1.x, seed_location1.y]
    l2 = im[seed_location2.x, seed_location2.y]
    count = np.sqrt(np.sum(np.square(l1 - l2)))
    return count


# import Image
im = cv2.imread(r'C:\Users\Lenovo\Desktop\2073.jpg')
cv2.destroyAllWindows()
im_shape = im.shape
height = im_shape[0]
width = im_shape[1]

# 标记,判断种子是否已经生长
img_mark = np.zeros([height, width])

# 建立空的图像数组,作为一类
img_re = im.copy()
for i in range(height):
    for j in range(width):
        img_re[i, j][0] = 0
        img_re[i, j][1] = 0
        img_re[i, j][2] = 0
# 取一点作为种子点
seed_list = []
seed_list.append(Point(15, 15))
T = 7  # 阈值
class_k = 1  # 类别
# 生长一个类
while (len(seed_list) > 0):
    seed_tmp = seed_list[0]
    # 将以生长的点从一个类的种子点列表中删除
    seed_list.pop(0)

    img_mark[seed_tmp.x, seed_tmp.y] = class_k

    # 遍历8邻域
    for i in range(8):
        tmpX = seed_tmp.x + connects[i].x
        tmpY = seed_tmp.y + connects[i].y

        if (tmpX < 0 or tmpY < 0 or tmpX >= height or tmpY >= width):
            continue
        dist = get_dist(seed_tmp, Point(tmpX, tmpY))
        # 在种子集合中满足条件的点进行生长
        if (dist < T and img_mark[tmpX, tmpY] == 0):
            img_re[tmpX, tmpY][0] = im[tmpX, tmpY][0]
            img_re[tmpX, tmpY][1] = im[tmpX, tmpY][1]
            img_re[tmpX, tmpY][2] = im[tmpX, tmpY][2]
            img_mark[tmpX, tmpY] = class_k
            seed_list.append(Point(tmpX, tmpY))

# 输出图像
cv2.imshow('OUTIMAGE', img_re)
cv2.waitKey(0)
cv2.destroyAllWindows()

分水岭算法

任意的灰度图像可以被看做是地质学表面,高亮度的地方是山峰,低亮度的地方是山谷。
在这里插入图片描述
给每个孤立的山谷(局部最小值)不同颜色的水(标签),当水涨起来,根据周围的山峰(梯度), 不同的山谷也就是不同的颜色会开始合并,要避免山谷合并,需要在水要合并的地方建立分水岭, 直到所有山峰都被淹没,所创建的分水岭就是分割边界线,这个就是分水岭的原理。
算法步骤

  1. 将白色背景编程黑色背景 - 目的是为了后面变的变换做准备
  2. 使用filter2D与拉普拉斯算子实现图像对比度的提高
  3. 转为二值图像4. 距离变换
  4. 对距离变换结果进行归一化[0-1]之间
  5. 使用阈值,在此二值化,得到标记
  6. 腐蚀每个peak erode
  7. 发现轮廓 findContours
  8. 绘制轮廓 drawContours
  9. 分水岭变换 watershed
  10. 对每个分割区域着色输出结果
import cv2
import numpy as np

# Step1. 加载图像
img = cv2.imread(r'C:\Users\Lenovo\Desktop\2073.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Step2.阈值分割,将图像分为黑白两部分
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# cv2.imshow("thresh", thresh)

# Step3. 对图像进行“开运算”,先腐蚀再膨胀
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# cv2.imshow("opening", opening)

# Step4. 对“开运算”的结果进行膨胀,得到大部分都是背景的区域
sure_bg = cv2.dilate(opening, kernel, iterations=3)

# Step5.通过distanceTransform获取前景区域
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)  # DIST_L1 DIST_C只能 对应掩膜为3    DIST_L2 可以为3或者5
print(dist_transform.max())
ret, sure_fg = cv2.threshold(dist_transform, 0.1 * dist_transform.max(), 255, 0)

# Step6. sure_bg与sure_fg相减,得到既有前景又有背景的重合区域   #此区域和轮廓区域的关系未知
sure_fg = np.uint8(sure_fg)
unknow = cv2.subtract(sure_bg, sure_fg)

# Step7. 连通区域处理
ret, markers = cv2.connectedComponents(sure_fg, connectivity=8)  # 对连通区域进行标号  序号为 0 - N-1
# print(markers)
print(ret)
markers = markers + 1  # OpenCV 分水岭算法对物体做的标注必须都 大于1 ,背景为标号 为0  因此对所有markers 加1  变成了  1  -  N
# 去掉属于背景区域的部分(即让其变为0,成为背景)
# 此语句的Python语法 类似于if ,“unknow==255” 返回的是图像矩阵的真值表。
markers[unknow == 255] = 0

# Step8.分水岭算法
markers = cv2.watershed(img, markers)  # 分水岭算法后,所有轮廓的像素点被标注为  -1
# print(markers)

img[markers == -1] = [0, 0, 255]  # 标注为-1 的像素点标 红
cv2.imshow("dst", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

特征与检测

图像特征是图像中独特的,易于跟踪和比较的特定模板或特定结构。特征就是有意义的图像 区域,该区域具有独特 性或易于识别性
图像特征提取与匹配是计算机视觉中的一个关键问题,在目标检测 物体识别 三维重建 图像配准 图像理解等具体应用中发挥着重要作用。
图像特征主要有图像的颜色特征、纹理特征、形状特征和空间关系特征。
颜色特征
颜色特征是一种全局特征,描述了图像或图像区域所对应的景物的表面性质
颜色特征描述方法:颜色直方图、色彩空间、颜色分布
纹理特征
纹理特征也是一种全局特征,它也描述了图像或图像区域所对应景物的表面性质但由于纹理只是一种物体表面的特性,并不能完全反映出物体的本质属性, 所以仅仅利用纹理特征是无法获得高层 次图像内容的。
形状特征
形状特征有两类表示方法, 一类是轮廓特征,另一类是区域特征 图像的轮廓特征主要针对物体的外边界, 而图像的区域特征则描述了是图像中的局部形状特征。
空间关系特征
图像中分割出来的多个目标之间的相互的空间位置或相对方向关系。
这些关系也可分为连接/邻接关系、交叠/重叠关系和包含/独立关系等。

形状特征

方向梯度直方图(Histogram of Oriented Gradient, HOG)

HOG征是一种在计算 机视觉和图像处理中用来进行物体检测的特征描述子,通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。
主要思想:在一副图像中,目标的形状能够被梯度或边缘的方向密度分布很好地描述。
HOG实现过程
(1)灰度化(将图像看做一个x,y,z(灰度)的三维图像);
(2)采用Gamma对输入图像进行颜色空间的标准化(归一化);
(3)计算图像每个像素的梯度(包括大小和方向);
(4)将图像划分成小cells;
(5)统计每个cell的梯度直方图(不同梯度的个数),得到cell的描述子;
(6)将每几个cell组成一个block,得到block的描述子;
(7)将图像image内的所有block的HOG特征descriptor串联起来就可以得到HOG特征,该特征向量就是用来目标检测或分类的特征。

import cv2
import numpy as np


# 判断矩形i是否完全包含在矩形o中
def is_inside(o, i):
    ox, oy, ow, oh = o
    ix, iy, iw, ih = i
    return ox > ix and oy > iy and ox + ow < ix + iw and oy + oh < iy + ih


# 对人体绘制颜色框
def draw_person(image, person):
    x, y, w, h = person
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 255), 2)


img = cv2.imread("people.jpg")
hog = cv2.HOGDescriptor()  # 启动检测器对象
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())  # 指定检测器类型为人体
found, w = hog.detectMultiScale(img, 0.1, (2, 2))  # 加载并检测图像

# 丢弃某些完全被其它矩形包含在内的矩形
found_filtered = []
for ri, r in enumerate(found):
    for qi, q in enumerate(found):
        if ri != qi and is_inside(r, q):
            break
    else:
        found_filtered.append(r)
# 对不包含在内的有效矩形进行颜色框定
for person in found_filtered:
    draw_person(img, person)
cv2.imshow("people detection", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

角点检测

在现实世界中,角点对应于物体的拐角,道路的十字路口、丁字路口等。从图像分析的角度来定义角点可以有以下两种定义:角点可以是两个边缘的交点;角点是邻域内具有两个主方向的特征点。前者通过图像边缘计算,计算量大,图像局部变化会对结果产生较大的影响,后者基于图像灰度的方法通过计算点的曲率及梯度来检测角点。
在这里插入图片描述
(1)角点所具有的特征:轮廓之间的交点;对于同一场景,即使视角发生变化,通常具备稳定性质的特征;该点附近区域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化;
(2)性能较好的角点:检测出图像中“真实”的角点、准确的定位性能、很高的重复检测率、噪声的鲁棒性、较高的计算效率。

Harris

在这里插入图片描述

# cv2.cornerHarris() 可以用来进行角点检测
# 参数:输入图像、kernel_size,sobel_kernel_size。Harris角点检测方程中的自由参数,取值参数为 [0,04,0.06]

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

img = cv2.imread(r'C:\Users\Lenovo\Desktop\2073.jpg')
# RGB格式的img
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 灰度格式的img
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst_block9_ksize19 = cv2.cornerHarris(gray, 9, 19, 0.04)
img1 = np.copy(img)
img1[dst_block9_ksize19 > 0.01 * dst_block9_ksize19.max()] = [0, 0, 255]

dst_block5_ksize19 = cv2.cornerHarris(gray, 5, 19, 0.04)
img2 = np.copy(img)
img2[dst_block5_ksize19 > 0.01 * dst_block5_ksize19.max()] = [0, 0, 255]

dst_block9_ksize5 = cv2.cornerHarris(gray, 9, 5, 0.04)
img3 = np.copy(img)
img3[dst_block9_ksize5 > 0.01 * dst_block9_ksize5.max()] = [0, 0, 255]

dst_block9_ksize31 = cv2.cornerHarris(gray, 9, 31, 0.04)
img4 = np.copy(img)
img4[dst_block9_ksize31 > 0.01 * dst_block9_ksize31.max()] = [0, 0, 255]

dst_block9_ksize19_k6 = cv2.cornerHarris(gray, 9, 19, 0.06)
img5 = np.copy(img)
img5[dst_block9_ksize19_k6 > 0.01 * dst_block9_ksize19_k6.max()] = [0, 0, 255]

dst_block9_ksize19_k6_1e_5 = cv2.cornerHarris(gray, 9, 19, 0.06)
img6 = np.copy(img)
img6[dst_block9_ksize19_k6_1e_5 > 0.00001 * dst_block9_ksize19_k6_1e_5.max()] = [0, 0, 255]

titles = ["Original", "block9_ksize19", "dst_block5_ksize19", "dst_block9_ksize5", "dst_block9_ksize31",
          "dst_block9_ksize19_k6", "dst_block9_ksize19_k6_1e_5"]
imgs = [img, img1, img2, img3, img4, img5, img6]
for i in range(len(titles)):
    plt.subplot(3, 3, i + 1), plt.imshow(imgs[i]), plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()
SIFT(Scale-invariant feature transform)

SIFT,即尺度不变特征变换算法(Scale-invariant feature transform, SIFT),是用于图像处理领域的一种算法SIFT具有尺度不变性,可在图像中 检测出关键点,是一种局部特征描述子。其应用范围包含物体辨识 机器人地图感知与导航 影像缝合 3D模型建立 手势辨识 影像追踪和动作比对。
SIFT特性
独特性,也就是特征点可分辨性高,类似指纹,适合在海量数据中匹配。
多量性,提供的特征多。
高速性,就是速度快。
可扩展,能与其他特征向量联合使用
SIFT特点
旋转、缩放、平移不变性
解决图像仿射变换,投影变换的关键的匹配
光照影响小
目标遮挡影响小
噪声景物影响小
算法步骤
(1)尺度空间(特征金字塔)极值检测点检测
(2)关键点定位:去除一些不好的特征点,保存下来的特征点能够满足稳定性等条件
(3)关键点方向参数:获取关键点所在尺度空间的邻域,然后计算该区域的梯度和方
向,根据计算得到的结果创建方向直方图,直方图的峰值为主方向的参数
(4)关键点描述符:每个关键点用一组向量(位置、尺度、方向)将这个关键点描述
出来,使其不随着光照、视角等等影响而改变
(5)关键点匹配:分别对模板图和实时图建立关键点描述符集合,通过对比关键点描
述符来判断两个关键点是否相同

# 定义提取器:cv.xfeatures2d.SIFT_create()
# 返回的关键点是一个带有很多不用属性的特殊 结构体,属性当中有坐标,方向 角度等
# 获取关键点:sift.detect()
# 使用sift.compute()函数来进行计算关键点描述符
# 如果未找到关键点,可使用函数sift.detectAndCompute()直接找到关键点并计算。
# 在第二个函数中,kp为关键点列表,des为numpy的数组,为关键点数目×128
import cv2
import numpy as np


img = cv2.imread(r'C:\Users\Lenovo\Desktop\2073.jpg')
gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)#找到关键点
img=cv2.drawKeypoints(gray,kp,img)#绘制关键点
cv2.imshow('sp',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

纹理特征

LBP(Local Binary Pattern)

LBP(Local Binary Pattern,局部二值模式),是一种用来描述图像局部纹理特征的算子;它具有旋转不变性和灰度不变性等显著的优点。
LBP算子定义在一个3 33 3的窗口内,以窗口中心像素为阈值,与相邻的8个像素的 灰度值比较,若周围的像素值大于中心像素值,则该位置被标记为1;,否则标记为0 如此可以得到一个8位二进制数(通常还要转换为10进制,即LBP码,共256种),将这 个值作为窗口中心像素点的LBP值,以此来反应这个3 3区域的纹理信息。
在这里插入图片描述在这里插入图片描述
LBP记录的是中心像素点与领域像素点之间的差值,当光照变化引起像素灰度值同增同减时,LBP变化并不明显,LBP对与光照变化不敏感,LBP检测的仅仅是图像的纹理信息。

# LBP
def LBP(src_path):
    src = cv.imread(src_path, 0)
    height = src.shape[0]
    width = src.shape[1]
    dst = src.copy()
    lbp_value = np.zeros((1, 8), dtype=np.uint8)
    # print(lbp_value)
    neighbours = np.zeros((1, 8), dtype=np.uint8)
    # print(neighbours)
    for x in range(1, width - 1):
        for y in range(1, height - 1):
            neighbours[0, 0] = src[y - 1, x - 1]
            neighbours[0, 1] = src[y - 1, x]
            neighbours[0, 2] = src[y - 1, x + 1]
            neighbours[0, 3] = src[y, x - 1]
            neighbours[0, 4] = src[y, x + 1]
            neighbours[0, 5] = src[y + 1, x - 1]
            neighbours[0, 6] = src[y + 1, x]
            neighbours[0, 7] = src[y + 1, x + 1]
            center = src[y, x]
            for i in range(8):
                if neighbours[0, i] > center:
                    lbp_value[0, i] = 1
                else:
                    lbp_value[0, i] = 0

            lbp = lbp_value[0, 0] * 1 + lbp_value[0, 1] * 2 + lbp_value[0, 2] * 4 + lbp_value[0, 3] * 8 \
                  + lbp_value[0, 4] * 16 + lbp_value[0, 5] * 32 + lbp_value[0, 6] * 64 + lbp_value[0, 7] * 128

            dst[y, x] = lbp

    return dst

模板匹配

# 模板匹配
# 模板匹配是一种最原始的模式识别方法,研究特定目标图像在整体的定位
# 算法核心:匹配度计算
# cv.matchTemplate(image,templ.method)
# 待搜索图像,模板图像,计算匹配程度所用函数
# method具体方法
# TM_CCORR_NORMED是标准相关性匹配,采用模板和图像间的乘法操作,数越大表示匹配程度越高
# TM_SQDIFF_NORMED是标准平方差匹配,通过计算两图之间平方差来进行匹配
# TM_CCOEFF_NORMED是标准相关系数匹配,两图减去各自平均值之外,还要各自除以各自的方差。将模板对均值的相对值与图像对其均值的相关值进行匹配,1完美,-1糟糕,0无关
def template_demo(tpl, target):
    methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED]  # 3种模板匹配方法
    th, tw = tpl.shape[:2]
    for md in methods:
        # print(md)
        result = cv.matchTemplate(target, tpl, md)
        # print(result.shape)
        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
        print(min_val, max_val, min_loc, max_loc)
        if md == cv.TM_SQDIFF_NORMED:
            tl = min_loc
        else:
            tl = max_loc
        br = (tl[0] + tw, tl[1] + th)  # br是矩形右下角的点的坐标
        cv.rectangle(target, tl, br, (0, 0, 255), 2)
        cv.namedWindow("match-" + np.str(md), cv.WINDOW_NORMAL)
        cv.imshow("match-" + np.str(md), target)
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZRX_GIS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值