列表

课程大纲

  • 图像基础学习
  • 图像基本运算
  • 基本图形的绘制
  • 图像基本绘制
  • 图像基本变换
  • 滤波器
  • 形态学
  • 轮廓查找
  • 特征点检测与匹配
  • 图像的分割和修复
  • 机器学习

课程收获

  • 添加水印
  • 车辆检测
  • 图像拼接
  • 车牌识别
  • 人脸识别
  • 图像分类
  • OpenCV的机制

进阶课程

  • 机器学习 -- 如何训练模型
  • ffmpeg -- 滤波器
  • webRT -- 实时通讯领域
  • OpenGL --图形学的课程,与图像学的课程 是属于不同概念

安装

# 查看pip版本
python -m pip --version
# 安装
python -m pip install numpy matplotlib opencv-python

python  -m  pip install  --upgrade pip

显示窗口

# https://www.bilibili.com/video/BV1Mv4y1M7gJ?p=118&spm_id_from=pageDriver&vd_source=010173c6f35c758e74dd6593e5722af0

import cv2

img = cv2.imread('./public/images/zheng.png')
cv2.imshow('img', img)
cv2.waitKey(0)


# 创建和显示窗口

# namedWindow()
# imshow()
# destroyAllWindows()
# resizeWindow

cv2.namedWindow('NEW', cv2.WINDOW_AUTOSIZE)
cv2.imshow('img', 0)

键盘退出

import cv2


# 创建和显示窗口

# namedWindow()
# imshow()
# destroyAllWindows()
# resizeWindow

cv2.namedWindow('NEW', cv2.WINDOW_NORMAL)
# cv2.resizeWindow('NEW', 500, 500)
cv2.imshow('NEW', cv2.imread('./public/images/zheng.png'))

key = cv2.waitKey(0)
# key 16 进制  获取了 8 进制  后与q的ASCII码进行比对
if (key & 0xFF == ord('q')):
    cv2.destroyAllWindows()

保存图片

# 保存图片

import cv2


# 创建和显示窗口

# namedWindow()
# imshow()
# destroyAllWindows()
# resizeWindow

cv2.namedWindow('NEW', cv2.WINDOW_NORMAL)
# cv2.resizeWindow('NEW', 500, 500)

img = cv2.imread('./public/images/zheng.png')

while True:
    cv2.imshow('NEW', img)
    key = cv2.waitKey(0)
    # key 16 进制  获取了 8 进制  后与q的ASCII码进行比对
    if (key & 0xFF == ord('q')):
        break
    elif (key & 0xFF == ord('c')):
        cv2.imwrite('./public/images/write.png', img)
    else:
        print(key)
cv2.destroyAllWindows()

视频采集

# 视频采集


# VideoCapure() 虚拟采集器,采取第一个设备号
# cap.read() 将视频帧读取为图片帧
# cap.release() 释放资源

import cv2


cv2.namedWindow('video', cv2.WINDOW_NORMAL)

# 0 代表的是第一个视频设备
# 或者改为视频文件所在的路径,即可更改为从视频文件中读取。
cap = cv2.VideoCapture(0)

while True:
    # 从摄像头获取视频帧 第一个:  为状态值,读到帧 为true 第二个为视频帧
    ret, frame = cap.read()
    cv2.imshow('video', frame)

    # 等待10ms而已。
    key = cv2.waitKey(1)
    if (key & 0xFF == ord('q')):
        break
    else:
        print(key)

cap.release()
cv2.destroyAllWindows()

视频录制

# 视频录制


# VideoWriter
# 参数一为 输出文件,参数二:指定多媒体文件格式(VidwoWriter_fourcc)参数三: 帧率,参数四:分辨率(查看电脑的摄像头的分辨率是多少进行设置)
# write
# release


# 视频采集


# VideoCapure() 虚拟采集器,采取第一个设备号
# cap.read() 将视频帧读取为图片帧
# cap.release() 释放资源

import cv2

fourcc = cv2.VideoWriter_fourcc(*'MJPG')

vw = cv2.VideoWriter('./out.mp4', fourcc, 25, (640, 360))


cv2.namedWindow('video', cv2.WINDOW_NORMAL)


# 0 代表的是第一个视频设备
# 或者改为视频文件所在的路径,即可更改为从视频文件中读取。
cap = cv2.VideoCapture(0)

while cap.isOpened():
    # 从摄像头获取视频帧 第一个:  为状态值,读到帧 为true 第二个为视频帧
    ret, frame = cap.read()
    if (ret):
        cv2.imshow('video', frame)
        vw.write(frame)
        # 等待10ms而已。
        key = cv2.waitKey(1)
        if (key & 0xFF == ord('q')):
            break
        else:
            print(key)
    else:
        break


cap.release()
vw.release()
cv2.destroyAllWindows()

鼠标回调函数

# 设置鼠标回调函数

# setMouseCallback( winname, callback, userdata)
# callback (event , x ,y ,flags, userdata)
# event :鼠标移动,按下左键右键等,
# flags:键盘ctrl shift alt + 组合键

import cv2
import numpy as np


def mouse_call_back(event, x, y, flags, userdata):
    print(event, x, y, flags, userdata)


cv2.namedWindow('mouse', cv2.WINDOW_NORMAL)

cv2.resizeWindow('mouse', 640, 360)

cv2.setMouseCallback('mouse', mouse_call_back, '123')

img = np.zeros((360, 640, 3), np.uint8)

while True:
    cv2.imshow('mouse', img)
    key = cv2.waitKey(1)
    if key & 0xFF == ord('q'):
        break
cv2.destroyAllWindows()

trackBar控件

# trackBar 控件

# createTrackbar()
# 参数:trackbarname,winname
# 参数: value:trackbar 当前值
# 参数:count: 最小值为0 最大值为count
# 参数:callback ,userdata

# getTrackbarPos()
# 参数:trackbarname:,winname
# 输出: 当前值

import cv2
import numpy as np


def call_back(value):
    print(value)


cv2.namedWindow('trackbar', cv2.WINDOW_NORMAL)
cv2.createTrackbar('R', 'trackbar', 0, 255, call_back)
cv2.createTrackbar('G', 'trackbar', 0, 255, call_back)
cv2.createTrackbar('B', 'trackbar', 0, 255, call_back)

# 纯黑色图片 3 层
img = np.zeros((480, 360, 3), np.uint8)

while True:

    r = cv2.getTrackbarPos('R', 'trackbar')
    g = cv2.getTrackbarPos('G', 'trackbar')
    b = cv2.getTrackbarPos('B', 'trackbar')
    img[:] = [r, g, b]
    cv2.imshow('trackbar', img)
    key = cv2.waitKey(10)
    if key & 0xFF == ord('q'):
        break
cv2.destroyAllWindows()

色彩空间

# 色彩空间
# 像素访问
# 矩阵的 + - *  /
# 基本图形的绘制

# RGB: 人眼的色彩空间
# OpenCV 默认使用BGR
# HSV/HSB/HSL
# HSV: 色相 饱和度 明亮度
# YUV:

# HSV
# Hue: 色相,即色彩,如红色,蓝色,
# Saturation : 饱和度,颜色的纯度
# Value: 明度
# ('./public/images/HSV001.png')
# ('./public/images/HSV002.png')
# ('./public/images/HSV003.png')


# HSL
# 亮度不同
# HUE SATURATION LIGHT
# ('./public/images/HSL HSV 的区别.png')

# YUV: 色彩空间转换,主要用于视频中。

# YUV4:2:0
# YUV4:2:2
# YUV4:4:4
# 4个Y 2个U 0 个V
# 黑白只有Y 。彩色兼容黑白电视产生YUV


# 色彩空间转换

import cv2

cv2.namedWindow('color', cv2.WINDOW_NORMAL)
img = cv2.imread('./public/images/HSV001.png')


def callback(values):
    print(values)


colorspaces = [cv2.COLOR_BGR2RGB, cv2.COLOR_BGR2RGBA,
               cv2.COLOR_RGB2GRAY, cv2.COLOR_BGR2HSV_FULL]

img = cv2.imread('./public/images/HSV001.png')

cv2.createTrackbar('curcolor', 'color', 0, len(colorspaces), callback)


while True:
    index = cv2.getTrackbarPos('curcolor', 'color')

# 颜色空间转换API
    cvt_img = cv2.cvtColor(img, colorspaces[index-1])
    cv2.imshow('color', cvt_img)
    key = cv2.waitKey(10)
    if key & 0xFF == ord('q'):
        break
cv2.destroyAllWindows()

Numpy基本

# Numpy
# OpenCV中用到的矩阵都要换换成Numpy的数组

# Numpy 基本操作

# 操作图片相当于操作矩阵
# 创建矩阵
# 检索与赋值[y , x]
# 获取子数组 [ :,:]

# 创建数组 array()
# 创建全0数组  zeros()/ones
# 创建全指数组 full()
# 创建单元数组  identity/eye() 正方形/非正方形

import cv2
import numpy as np

# 一维数组
a = np.array([2, 3, 4])
# 二维数组
c = np.array([[1, 2], [3, 4]])

print(a, '\n', c)

d = np.zeros((4, 8, 6), np.uint8)
# 行的个数,列的个数,通道数/层数  y, x 对于RGB 来说是有3个通道的
# np.unit8 矩阵中的数据类型 ,最大值为255

print(d)

f = np.ones((4, 8, 6), np.uint8)
print(f)

e = np.full((4, 8, 6), 255, np.uint8)
print(e)

# 单位矩阵
g = np.identity(3)
print(g)

# 非单位矩阵 k 从第2 个开始
s = np.eye(3, 6, k=2)
print(s)

# Numpy 基本操作

'''
创建矩阵
检索与赋值[y,x]
从 0 开始 ,注意和常见的[x,y] 有所区别
[y,x,channel] channel 通道数,对于彩色图像来说是3层的,灰色是1层的
获取子数组[:,:]
'''
# 全部是0 的3 层矩阵

img = np.zeros((480, 640, 3), np.uint8)

# 检索式3层的
print(img[100][100])

count = 0


while count < 200:
    # 检索一:
    # BGR B 的 255
    img[count, 100] = 255
    # 0 BGR 的B
    img[count, 100, 0] = 255
    # 1 BGR 的G
    img[count, 100, 1] = 255
    count = count + 1
    # 如果3层都是255 ,则是白色

    # 检索二:
    # BGR 分别为 0 0 255
    img[count, 100] = [0, 0, 255]

# ROI 操作
roi = img[100:200, 100:200]
# BGR 设置为红色
roi[:, :] = [0, 0, 255]

# cv2.imshow('img', img)
cv2.imshow('img', roi)
key = cv2.waitKey(0)
if key & 0xFF == ord('q'):
    cv2.destroyAllWindows()


# 矩阵操作3 ROI
'''
[y1:y2,x1:x2] 获取子矩阵
[:,:]获取矩阵的整个矩阵
'''

OpenCV 重要结构体Mat

# OpenCV 重要的结构体Mat

# 矩阵 ,可以有多通道,如果是灰色的话,只有1 个通道, 如果是彩色的,则有3 个通道

# mat有什么好处,操作矩阵方便

# 底层实现: header   - > data
'''
class cv_exports mat{
    public:
      int dims: 维数
      int rows,cols ;行列数
      uchar * data; 存储数据的指针
      int *refcount; 引用计数
      depth: 像素的位数 ,彩色是8位数据
      size: 矩阵的大小
      channel: 通道数,RGB是3  
      type: dep+dt+chs CV_8UC3
      
};
'''

# mat 深拷贝 浅拷贝
# 默认是属于 浅拷贝
# Mat A
# A = imread(file,IMAREAD_COLOR)
# Mat B(A)
# 深拷贝
'''
cv::Mat::clone() 
cv::Mat::copyTo()
copy() 接口 ,底层会调用上述的两个深拷贝接口
'''


import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')

# 浅拷贝
img2 = img
img3 = img.copy()

img[10:100, 10:100] = [0, 0, 255]

cv2.imshow('img', img)
cv2.imshow('img2', img2)
# 深拷贝
cv2.imshow('img3', img3)
# cv2.waitKey(0)

# 图像的属性

# 高度 长度  通道数
print(img.shape)
# size = 高度 * 长度  * 通道数
print(img.size)
# 图片位深度
print(img.dtype)

# 通道的分离和合并

'''
split(mat)
merge((ch1,ch2,...))
'''

img4 = np.zeros((480, 360, 3), np.uint8)

cv2.imshow('img4', img4)

b, g, r = cv2.split(img4)
b[10:100, 10:100] = 255

cv2.imshow('b', b)

new = cv2.merge((b, g, r))
cv2.imshow('new', new)

cv2.waitKey(0)

OpenCV 绘制图形

# OpenCV 绘制图形
'''
line(img,开始点,结束点,颜色,线宽,线型)

ellipse(img,中心点,(长的一半,宽的一半),倾斜角度,从哪个角度开始,到哪个角度结束,颜色,线宽,线性)
polylines(img,点集(32位的),是否闭环,颜色,...)

画文本
putText(img,字符串,起始点,字体,字号,...)
字体:0 1 2 3 4 5 6 7 8 9 ...16
'''

import numpy as np

import cv2
# zeros坐标代表:[y,x]
img = np.zeros((600, 600, 3), np.uint8)
# line 坐标代表:[x,y]
# 10 是线的宽度, 4是带锯齿的线,默认是8 ,可选值由   -1 4 8 16
cv2.line(img, (10, 30), (100, 300), (255, 255, 255), 10, 4)
# 画矩形
cv2.rectangle(img, (10, 10), (100, 100), (255, 255, 255), -1)
# 画圆形
cv2.circle(img, (320, 240), 100, (255, 255, 255))
cv2.circle(img, (320, 240), 5, (255, 255, 255), -1)
# 画椭圆 -1 填充
cv2.ellipse(img, (120, 180), (60, 100), 50, 0, 180, (255, 255, 255), -1)
# 画多边形
# 点集合
pts = np.array([(300, 10), (150, 100), (450, 100)], np.int32)
# 画边
cv2.polylines(img, [pts], True, (0, 255, 255))
# 填充
cv2.fillPoly(img, [pts], (255, 255, 0))

# 画文本
cv2.putText(img, 'English ...', (60, 60),
            cv2.FONT_ITALIC, 3, (255, 255, 255))


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

鼠标绘制图片

# 实现鼠标绘制图片
# shape:0 输入l 画直线
# shape:1 输入r 画矩形
# shape:2 输入c 画圆形
import cv2
import numpy as np

shape = 0
start = (0, 0)


cv2.namedWindow('mouse', cv2.WINDOW_NORMAL)


img = np.zeros((360, 640, 3), np.uint8)


def mouse_callback(event, x, y, flags, userdata):
    global shape, start
    print(x, y)
    if (event & cv2.EVENT_LBUTTONDOWN == cv2.EVENT_LBUTTONDOWN):
        start = (x, y)
    elif (event & cv2.EVENT_LBUTTONUP == cv2.EVENT_LBUTTONUP):
        if shape == 0:
            cv2.line(img, start, (x, y), (0, 0, 255))
        elif shape == 1:
            cv2.rectangle(img, start, (x, y), (0, 0, 255))
        elif shape == 2:
            a = (x - start[0])
            b = (y - start[1])
            r = int((a**2 + b**2) ** 0.5)
            cv2.circle(img, start, r, (0, 0, 255))
        else:
            print('error')


cv2.setMouseCallback('mouse', mouse_callback, "123")
while True:
    cv2.imshow('mouse', img)
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        break
    elif key == ord('c'):
        shape = 2
    elif key == ord('r'):
        shape = 1
    elif key == ord('l'):
        shape = 0
cv2.destroyAllWindows()

图像运算- 图像加减乘除

# 基本图像运算与处理
# 形态学
# 轮廓查找


# 图像的相加,相减 乘除
# 加:曝光更多, 减 曝光更少  乘 曝光比加更厉害  除  曝光比减更厉害
import cv2
import numpy as np

# 图形的运行 = 矩阵的运算
# 因此,加法运算的两张图必须是相等的

hsv0001 = cv2.imread('./public/images/HSV001.png')

print(hsv0001.shape)

img = np.ones((hsv0001.shape[0], hsv0001.shape[1], 3), np.uint8) * 50

result = cv2.add(hsv0001, img)
# 图像的减法
result2 = cv2.subtract(result, img)
result3 = cv2.multiply(hsv0001, img)
result4 = cv2.divide(hsv0001, img)


cv2.imshow('result2', result2)
cv2.imshow('result3', result3)

cv2.imshow('result4', result4)

cv2.imshow('origin', hsv0001)
cv2.imshow('result', result)


# 图像的溶合
# 只有两张图的属性是一样的才可以融合
'''
A 图像  B 图像
alpha beta 是权重
addWeighted(A ,alpha,B,bate,gamma)
gamma 静态权重
'''


i3 = cv2.addWeighted(hsv0001, 0.7, img, 0.3, 0)
cv2.imshow('i3', i3)

# 图像位运行

'''
没有进位

与 只有两个为1 结果才为1
或 只要由一个1 结果就为1
非 取反
异或 只有两个不一样才为1 不然为0 

'''

n1 = np.zeros((500, 500), np.uint8)

n1[200:300, 200:300] = 255
n2 = np.zeros((500, 500), np.uint8)

n2[250:350, 250:350] = 255

cv2.imshow('n1', n1)
cv2.imshow('n2', n2)

not_i = cv2.bitwise_not(n1)
and_i = cv2.bitwise_and(n1, n2)
or_i = cv2.bitwise_or(n1, n2)
xor_i = cv2.bitwise_xor(n1, n2)

cv2.imshow('not', not_i)
cv2.imshow('and', and_i)
cv2.imshow('or', or_i)
cv2.imshow('xor', xor_i)


cv2.waitKey(0)

图像变换-缩放翻转旋转透视仿射

# 图像变换

'''
图像的缩放
图像翻转
图像旋转
'''
'''
改变的是窗口内图像的大小
resize(src,dst,dsize,fx,fy,interpolation)
x轴的缩放因子
y轴的缩放因子
定义了dsize,就不需要定义fx fy
interpolation: 插值算法
Inter_nearest : 邻近插值,速度快,效果差
inter_linear : 双线性插值,原图中的4个点,默认算法
inter_cubic :三次插值,原图中的16个点,但是花费时间比较长
inter_area :效果最好,但是时间较长
'''


import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
# 缩放0.5 倍
new = cv2.resize(img, (int(
    img.shape[1] * 0.5), int(img.shape[0] * 0.5)), None, interpolation=cv2.INTER_AREA)

cv2.imshow('new', new)


'''
图像反转
flip(img,flipCode)
0 上下翻转
> 0 左右翻转
<0 上下+左右翻转
'''
fanzhuan = cv2.flip(new, 2)
cv2.imshow('fanzhuan', fanzhuan)
'''
图像旋转
rotate(img,rotateCode)
顺时针90度  180度旋转  逆时针90度
ROTATE_90_CLOCKWISE
ROTATE_180
ROTATE_90_COUNTERCLOCKWISE
'''
xuanzhuan = cv2.rotate(new, cv2.ROTATE_90_CLOCKWISE)

cv2.imshow('xuanzhuan', xuanzhuan)

'''
图像的仿射变换 
仿射变换是图像旋转、旋转、平移的总称
warpAffine(src,M,dsize,flags,mode,value)
M: 变换矩阵(重要)
dsize:变换后的大小
flags: 与resize中的插值算法一致 (一般采用默认值)
mode: 边界外推法标志 (一般采用默认值)
value: 填充边界的值(一般采用默认值)
'''

'''
平移矩阵
矩阵中的每个元素由(x,y) 组成
如果是横向平移,则是在 x上增加数
如果是纵向平移,则是在 y 上增加数
因此,其变换矩阵是 2x2 的矩阵
平移向量为2x1 的向量,所在平移矩阵为2x3矩阵
6:56
'''
'''
https://blog.csdn.net/lz0499/article/details/90578545
说明:[[1,0],[0,1]] 表示 一个 2x2的单元矩阵。100,200表示偏移量,表示x 和 y的偏移量
'''

M = np.float32([[1, 0, 100], [0, 1, 200]])
h, w, ch = new.shape
pingyi = cv2.warpAffine(new, M, (w, h))

cv2.imshow('pingyi', pingyi)


'''
变换矩阵
getRotationMatrix2D(center,angle,scale)
获取矩阵M ,而不需要自己去计算
center 中心点
angle 角度-按逆时针 
scale 缩放比例

'''

# 这里只是图像内的大小,画布大小不变
M2 = cv2.getRotationMatrix2D((w/2, h/2), 45, 0.75)
print(M2)
# 如果想改变信画布的尺寸,需要更改dsize
bianhuan = cv2.warpAffine(new, M2, (w, h))
cv2.imshow('bianhuan', bianhuan)

'''
第二中变换矩阵
由直角三角形的三个点获得的新的变换矩阵
getAffineTransform(src[],dst[])

'''
src = np.float32([[40, 30], [80, 30], [40, 30]])
dst = np.float32([[20, 40], [60, 50], [15, 30]])
bianhuan2 = cv2.getAffineTransform(src, dst)

cv2.imshow('bianhuan2', bianhuan2)

'''
透视变换
将一种坐标系变换为另一种坐标系
warpPerspective(img,M,dsize)
M 是变换矩阵
dsize是目标图像的大小
# 获取M , src dst 分别为四个点
getPersectiveTransform(src,dst)
'''
src = np.float32([[100, 110], [150, 160], [0, 50], [600, 800]])
dst = np.float32([[60, 80], [200, 250], [50, 100], [699, 899]])

M3 = cv2.getPerspectiveTransform(src, dst)
toushi = cv2.warpPerspective(new, M3, (w, h))
cv2.imshow('toushi', toushi)

cv2.waitKey(0)

图像滤波-卷积等相关概念

# 图像滤波
'''
一幅图像通过滤波器得到另一幅图像;
其中滤波器又称为卷积核,滤波的过程称为卷积

对图像进行扫描和乘法
卷积:原始图像与矩阵进行乘法运算

卷积的几个概念

卷积核的大小
锚点
边界扩充
步长

![]('./public/images/lvbo001.png')
![]('./public/images/lvbo002.png')

卷积核

卷积核一般为奇数,如3x3、5x5、7x7 等
一方面是增加padding的原因;
另一方面是保证锚点在中间,防止位置发生偏移的原因

卷积核大小的影响
在深度学习种,卷积核越大,看到的信息(感受野)越多
提取的特征越好,同时计算量也就越大

锚点
卷积核的正中心就是锚点
![]('./public/images/lvbo003.png')

边界扩充
当卷积核大于1 且不进行边界扩充,输出尺寸将相应缩小
当卷积核以标准方式进行边界扩充,则输出数据的空间尺寸将与输入相等

N = ( W - F +2P)/S +1
N 输出图像大小
W 源图大小
F 卷积核大小
P 扩充尺寸
S 步长大小,默认是1

低通滤波 和高通滤波

低通滤波可以去除噪音或平滑图像
高通滤波可以帮助查找图像的边缘,进行物体识别

图像卷积
filter2D(src,ddepth,kernel,anchor,delta,borderType)
ddepth:位深 ,当值为-1  的时候,表示输出的位深和输入的位深是一致的
kernel:卷积核 当卷积为低通滤波 ,当卷积为高通滤波,
anchor :锚点: 当值为-1 时候,则是输入的核对应的标准锚点
delta: 当卷积之后,可以相加的值,一般为0
borderType :边缘
'''
import cv2
import numpy as np


img = cv2.imread('./public/images/zheng.png')

# 5 x5 的矩阵,里面每个元素乘以 25分之一
kernel = np.ones((5, 5), np.float32) / 25

dst = cv2.filter2D(img, -1, kernel)

cv2.imshow('dst', dst)

'''
方盒滤波和均值滤波
这两种滤波器的卷积核是固定的.
('./public/images/001-方盒滤波卷积核.png')
参数a 是未知数

normalize = true , a = 1/W * H  均值滤波
normalize = false ,a = 1

当normalize == true 时, 方盒滤波 =平均滤波
一般取 normalize = true ,即方盒滤波一般是指 平均滤波

所以一般都是调用平均滤波的API

方盒滤波API
boxFilter(src ,ddepth,ksize,anchor,normalize,borderType)
#参数 原图,位深,卷积核大小,锚点,正常是否,边缘
当normalize =true时
均值滤波API
blur(src,ksize,anchor,borderType)
'''
fanghe = cv2.blur(img, (5, 5))

cv2.imshow('fanghe', fanghe)
'''
高斯滤波
('./public/images/002-高斯滤波.png')
('./public/images/003-高斯滤波.png')
('./public/images/004-高斯滤波.png')
中间权重大,旁边权重低
对高斯噪音进行处理

GaussianBlur ( img, kernel,sigmaX ,sigmaY...)
参数,原图,卷积核,
X,Y:最大范围到中心点有多少误差
('./public/images/005-高斯滤波.png')
https://blog.csdn.net/u013066730/article/details/123112159
'''

gaosi = cv2.GaussianBlur(img, (5, 5), sigmaX=1, sigmaY=1)
cv2.imshow('gaosi', gaosi)

'''
中值滤波
假设有一个数组[1556789],取其中的中间值作为卷积后的结果值
中值滤波的优点是对胡椒噪音效果明显(没错,就是胡椒噪音)

medianBlur(img,ksize)
'''
zhngzhi = cv2.imread('./public/images/006.png')


result = cv2.medianBlur(zhngzhi, 5)
cv2.imshow('zhngzhi', zhngzhi)
cv2.imshow('result', result)

'''
双边滤波
可以保留边缘,同时对边缘内的区域进行平滑处理
作用是进行美颜
之前的滤波不仅对图像区域误内无差别的处理,对边缘也处理.
('./public/images/007-双边滤波.png')

bilateralFilter(img,d,sigmaColor,sigmaSpace)
d: 直径


'''
shuangbian = cv2.bilateralFilter(img, 7, 20, 50)

cv2.imshow('shuangbian', shuangbian)


'''
高通滤波
常见的高通滤波

Sobel(索贝尔)-对噪音过滤很强,效果差,对边缘检测,只能求一个方向的边缘
Scharr(沙尔)-不可改变卷积核大小(3x3) 效果好,对边缘不可检测,只能求一个方向的边缘
Laplacian(拉普拉斯) ,能自己计算两个方向的边缘,但是没有降噪的功能


Solbel 算子

先向x方向求导
再向y方向求导
最终结果: |G| =|Gx|+|Gy|

Sobel(src,ddepth,dx,dy,ksize = 5,scale =1,delta = 0 ,borderType = Border_default)
当ksize = 3 ,则退化为沙尔
dx ,dy 只能设置其中一个为1,另外一个为0
当dx设置为1 时候,求的是对Y轴进行求导,算的是Y轴的边缘
当dy设置为1 时候,求的是对X轴进行求导,算的是X轴的边缘
scale :缩放比例

'''

sobel_old = cv2.imread('./public/images/008.png')
sobel1 = cv2.Sobel(sobel_old, -1, 1, 0, ksize=5)
sobel2 = cv2.Sobel(sobel_old, -1, 0, 1, ksize=5)
cv2.imshow('sobel1', sobel1)
cv2.imshow('sobel2', sobel2)

cv2.imshow('sobel', cv2.add(sobel2, sobel1))

'''
Scharr 沙尔算子

与Sobel类似,只不过使用的kernel值不同,kernel只支持3x3
Scharr只能求x方向或y方向的边缘
可以将更细小的线给识别出来
很少运用
Scharr(src,ddepth,dx,dy,)
'''

'''
Laplacian(拉普拉斯) 算子

缺点:对噪音敏感,一般需要先进行去噪再调用拉普拉斯算子
可以同时求两个方向
Laplacian(img,ddepth,ksize,scale=1,borderType =BORDER_DEFAULT)
'''

lap = cv2.Laplacian(sobel_old, -1)

cv2.imshow('lap', lap)

'''
边缘检测,终极大法

Canny
使用5x5高斯滤波消除噪音
计算图像梯度(0° 45 90 135)
取局部最大值
('./public/images/009-Canny阈值计算.png')

API
Canny(img,minVal,maxVal)

minVal maxVal:
min 越小:边缘越少 
max 只要超过最大值,都会认为是边缘,所以,max设置得越小,边缘会越多
min-max 之间的过大,则边缘会越少

'''

canny = cv2.Canny(sobel_old, 100, 200)
cv2.imshow('canny', canny)

cv2.waitKey(0)

形态学处理-腐蚀膨胀开闭运算顶帽黑帽

# 形态学处理
'''
基于图像形态进行处理的一些基本方法
这些处理方法基本是对二进制图像进行处理(黑白图像进行处理)
卷积核决定着图像处理后的效果

形态学图像处理

腐蚀:形态缩小
膨胀:形态放大

开运算:先腐蚀,后膨胀
闭运算:先膨胀,后腐蚀

顶帽:
黑帽:

'''

'''
图像二值化(黑白处理)

将图像中的每个像素变成两种值:如0,255

二值化有两种

全局二值化
局部二值化

threshod (img ,thresh,maxVal ,type)
img :图像:最好是灰度图

超过阈值的时候,便是 黑色的点
thresh:阈值 ,低于阈值,则是为0 .高于阈值,则变成maxVal
maxVal: 超过阈值,替换成 maxVal

type: thresh_binary 和 thresh_binary_inv 
thresh_trunc
thresh_tozero 和thresh_tozero_inv

'''

import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
# 生成灰度图片
huidu = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 结果,以及输出的图像
ret, dst = cv2.threshold(huidu, 180, 255, cv2.THRESH_BINARY)

cv2.imshow('quan', dst)

'''
自适应阈值
解决全局二值化的缺点:由于光照不均匀以及阴影的存在,,只有一个阈值会使得在阴影处的白色被二值化成黑色

adaptiveThreshold(img,maxVal,adaptiveMethod,Type,blockSize,C)
method:计算阈值的方法
ADAPTIVE_THRESH_MEAN_C :计算邻近区域的平均值
ADAPTIVE_THRESH_GUASSIAN_C 高斯窗口加权平均值 常用

type:thresh-binary 或 thresh-binary_inv

blockSize:自定义区域大小
C 常量,应从计算出的平均值或加权平均值中减去

'''

zishiying = cv2.adaptiveThreshold(
    huidu, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 3, 0)

cv2.imshow('zishiying', zishiying)

'''

2、膨胀(dilate)
膨胀就是求局部最大值的操作。从数学角度来说,就是将图像与核进行卷积,计算核B覆盖区域的像素点的最大值,并把这个最大值赋值给参考点指定的元素。这样就会使图像中的高亮区域逐渐增长。

3、腐蚀(erode)
腐蚀和膨胀是相反的操作,腐蚀是求局部最小值的操作。腐蚀操作会使图像中的高亮区逐渐减小。


腐蚀:苹果腐蚀
腐蚀的卷积核都是为1的矩阵

erode(img,kernel,iterations=1)
iterations: 腐蚀的次数

只有两者(黑底白图和卷积核)都是白色的时候,才能编程白色


'''
kernel = np.ones((3, 3), np.uint8)
fushi = cv2.erode(huidu, kernel, 9999)
cv2.imshow('fushi', fushi)

'''
形态学的卷积核
卷积核的类型
getStructuringElement(type,size)
type:Morph_rect ,矩形的,全1
Morph_ellipse 椭圆的矩阵,1组成一个圆形,其他为0
Morph_cross :十字架 的矩阵,中间一列和中间一行是1 ,其他都是0 
size: (3,3)、(5,5)

'''
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
kernel3 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
kernel4 = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))

print(kernel2)
print(kernel3)
print(kernel4)

'''
膨胀
卷积核越大,膨胀越大
只要有1 ,就能扩展。
dilate(img,kernel,iterations=1)
'''
r = cv2.dilate(huidu, cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)))

cv2.imshow('r', r)

'''
开运算=腐蚀+膨胀 (作用:腐蚀:先去除噪点,膨胀:恢复因去除噪点造成的损失)
闭运算=膨胀+腐蚀(作用:膨胀:先去除噪点,腐蚀:再恢复因去除噪点造成的损失)


开运算API 消除外部噪点 ,噪点越大,kernel越大
morphologyEx(img,MORPH_OPEN,KERNEL)
闭运算API
('./public/images/015-开运算-消除白噪点.png')
('./public/images/016-闭运算-消除白噪点.png')
消除白底的黑色噪点
morphologyEx(img,MORPH_CLOSE,KERNEL)

'''
kai = cv2.morphologyEx(huidu, cv2.MORPH_OPEN, kernel=kernel)
BI = cv2.morphologyEx(huidu, cv2.MORPH_CLOSE, kernel=kernel)


cv2.imshow('kai', kai)
cv2.imshow('BI', BI)

'''
形态学梯度
梯度 = 原图 - 腐蚀性
morphologyEx(img,MORPH_GRADIENT,kernel)
求:边缘
kernel比较大,腐蚀性比较大。边缘就不是特别清楚

'''
xingtaixue = cv2.morphologyEx(huidu, cv2.MORPH_GRADIENT, kernel=kernel)

cv2.imshow('xing', xingtaixue)


'''
顶帽运算
顶帽 = 原图 -  开运算
morphologyEx(img,MORPH_TOPHAT,kernel)
剩下黑底白色噪点
'''
ding = cv2.morphologyEx(huidu, cv2.MORPH_TOPHAT, kernel=kernel)

cv2.imshow('ding', ding)


'''
黑帽操作
黑帽 = 原图 -  闭运算
剩下白底的黑点
'''
h = cv2.morphologyEx(huidu, cv2.MORPH_BLACKHAT, kernel=kernel)

cv2.imshow('h', h)


'''
总结:
开运算,先腐蚀再膨胀,去除大图形外的小图形
闭运算:先膨胀再腐蚀,去除大图形内的小图形
梯度:求图形的边缘
顶帽:原图-开运算,得到大图形外的小图形
黑帽:原图-闭运算,得到大图形内的小图形


'''
cv2.waitKey(0)

图像轮廓

# 图像轮廓

# 什么是图像轮廓
# 具有相同颜色活泼强度的连续点的曲线
'''
图像轮廓的作用

可以用于图形分析
物体的识别和检测

注意点:
为了检测的准确性,需要先对图像进行二值化或Canny操作
画轮廓时会修改输入的图像

轮廓查找的API

findContours(img,mode,ApproximationMode...)

模式-mode:

Retr_external=0,表示之检测外轮廓
Retr_list=1,检测的轮廓不建立等级关系-常用
Retr_ccomp = 2 ,每层最多两级
Retr_tree = 3 ,按树形存储轮廓-常用
('./public/images/020-轮廓-EXTERNAL.png')
('./public/images/021-轮廓-LIST.png)
('./public/images/022-轮廓-CCOMP.png')
('./public/images/023-轮廓-TREE.png')

近似模式-ApproximationMode:

chain_approx_none :保存所有轮廓上的点
CHAIN_APPROX_SIMPLE 只保存角点

两个返回值:
contours 和hierarchy

contours:所有轮廓
hierarchy:轮廓间的层级关系

'''

'''
查找轮廓
'''

import cv2
origin = cv2.imread('./public/images/zheng.png')

cv2.imshow('origin', origin)
# 单通道
huidu = cv2.cvtColor(origin, cv2.COLOR_BGR2GRAY)
cv2.imshow('huidu', huidu)

# 由3通道转换为单通道
print(origin.shape)
print(huidu.shape)

# 二值化
ret, binary = cv2.threshold(huidu, 150, 255, cv2.THRESH_BINARY)
cv2.imshow('bin', binary)

# 返回值:所有轮廓值,以及轮廓之间的关系
contours, hierarchy = cv2.findContours(
    binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# print(contours, hierarchy)


# 如何绘制轮廓

'''
drawContours(img,contours,controurIdx,color,thickness)

contourIdx :设置顺序号, -1 表示绘制所有轮廓
color:轮廓颜色 
thicknes:线宽,-1 是全部填充

'''
# 可输入原图,或者二进制图,或者单通道图
new = cv2.drawContours(origin, contours, -1, (255, 255, 255), 1)

cv2.imshow('new', new)

'''
轮廓的面积和周长

contourArea(contour)
contour:轮廓

arcLength(curve,closed)
closed:是指闭合的还是非闭合的

'''

area = cv2.contourArea(contours[2])

print(area)
length = cv2.arcLength(contours[2], True)

print(length)


'''
多边形逼近与凸包
('./public/images/024-多边形逼近与凸包.png')

API

approxPolyDP(curve,epsilon,closed)
curve:轮廓
epsilion: 精度
closed:是否是闭合的轮廓

convexHull(points,clockwise,..)
clockwise:顺时针绘制

'''

hand = cv2.imread('./public/images/025-hand.png')

hand_gray = cv2.cvtColor(hand, cv2.COLOR_BGR2GRAY)

hand_ret, hand_bin = cv2.threshold(hand_gray, 150, 255, cv2.THRESH_BINARY)

hand_con, hand_hierarchy = cv2.findContours(
    hand_bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

hand_cond = cv2.drawContours(hand, hand_con, 0, (255, 255, 255), 1)
cv2.imshow('hand_cond', hand_cond)
print('hand_cond_len', hand_cond)

# 这里选取有问题
approx = cv2.approxPolyDP(hand_con[1], 20, True)


def drawShape(src, points):
    i = 0
    while i < len(points):
        if (i == len(points)-1):
            x, y = points[i][0]
            x1, y1 = points[0][0]
            cv2.line(src, (x, y), (x1, y1), (255, 255, 255), 1)
        else:
            x, y = points[i][0]
            x1, y1 = points[i+1][0]
            cv2.line(src, (x, y), (x1, y1), (255, 255, 255), 1)
        i = i+1


drawShape(hand, approx)

'''
外接矩阵

最大外接矩阵

最小外接矩阵
('./public/images/026-最大最小外接矩阵.png')

minAreaRect(points)

points:轮廓
返回值
RotatedRect  : 是一个结构体,旋转的矩阵,其中的角度非常关键 其中包括

x,y 起始点
width,height
angle:角度

boundingRect(array)
array:轮廓
返回值: Rect
x,y 起始点

width,height 
'''


cv2.waitKey(0)

图像矩阵


import cv2
import numpy as np

'''
外接矩阵

最大外接矩阵

最小外接矩阵
('./public/images/026-最大最小外接矩阵.png')

minAreaRect(points)

points:轮廓
返回值
RotatedRect  : 是一个结构体,旋转的矩阵,其中的角度非常关键 其中包括

x,y 起始点
width,height
angle:角度

boundingRect(array)
array:轮廓
返回值: Rect
x,y 起始点

width,height 
'''

hand = cv2.imread('./public/images/027-waijiejuzhen.png')

hand_gray = cv2.cvtColor(hand, cv2.COLOR_BGR2GRAY)

hand_ret, hand_bin = cv2.threshold(hand_gray, 150, 255, cv2.THRESH_BINARY)


hand_con, hand_hierarchy = cv2.findContours(
    hand_bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

r = cv2.minAreaRect(hand_con[1])


box = cv2.boxPoints(r)
box = np.int0(box)
cv2.drawContours(hand, [box], 0, (255, 255, 255), 2)

x, y, w, h = cv2.boundingRect(hand_con[1])
cv2.rectangle(hand, (x, y), (x+w, y+h), (255, 255, 0), 2)

cv2.imshow('hand', hand)

cv2.waitKey(0)

车辆识别

# 车辆识别
'''
加载视频
通过形态学识别车辆
对车辆进行统计
显示车辆统计信息


去背景

createBackgroundSubtractorMOG(...)
history = 200

预存 多少位帧 有可以判断作为背景

python -m pip install opencv-contrib-python --user

'''

import cv2
import numpy as np

bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG()


# 所有帧
cap = cv2.VideoCapture('./public/vedio/che.mp4')
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

# 视频是 (frame)
line_hight = 380
offsert = 6
cars = []
carno = 0

# 计算中心点


def center(x, y, w, h):
    x1 = int(w/2)
    y1 = int(h/2)
    cx = x+x1
    cy = y + y1
    return cx, cy


while True:
    ret, frame = cap.read()
    if (ret):
        # print(frame.shape)
        # 灰度化
        huidu = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 高斯去噪
        blur = cv2.GaussianBlur(huidu, (5, 5), 5)
        # 去背景
        mask = bgsubmog.apply(blur)
        # 形态学处理 腐蚀:去掉大物块 外的小物块
        erode = cv2.erode(mask, kernel)

        # 形态学处理 膨胀,恢复大小
        dilate = cv2.dilate(erode, kernel, iterations=3)

        # 闭运算去掉物体内部的小块

        close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel)

        # 查找轮廓
        # 所有轮廓,轮廓的关系
        con, cengji = cv2.findContours(
            close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        cv2.line(frame, (10, line_hight),
                 (480, line_hight), (255, 255, 255), 3)
        for (i, c) in enumerate(con):
            (x, y, w, h) = cv2.boundingRect(c)
            cv2.rectangle(frame, (x, y), (x+w, y+h),
                          (255, 255, 255), 2)
            # 如果> 25 才统计,这个需要加以调节
            if (w > 25 and h > 25):
                cpoint = center(x, y, w, h)
                cars.append(cpoint)
                for (x, y) in cars:
                    # 要有一条线
                    # 线有范围 6 ,上下6个像素点
                    # 从数组种减去
                    if ((y > line_hight - offsert) and (y < line_hight+offsert)):
                        carno += 1
                        cars.remove((x, y))
                        print(carno)

        cv2.putText(frame, "Cars Count:"+str(carno), (50, 50),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1)
      #  cv2.imshow('huidu', huidu)
      #  cv2.imshow('blur', blur)
     #   cv2.imshow('mask', mask)
     #   cv2.imshow('erode', erode)
       # cv2.imshow('dilate', dilate)
       # cv2.imshow('close', close)
        cv2.imshow('frame', frame)
    key = cv2.waitKey(1)
    if (key == 27):
        break


cap.release()
cv2.destroyAllWindows()

特征检测-Harris、Shi-Tomasi

# OpenCV特征检测
'''
应用场景:
图像搜索,如以图搜图
拼图游戏
图像拼接,将两长有关联的图拼接到一起

拼图方法
* 寻找特征
* 特征是唯一的
* 可追踪的
* 能比较的
* 平坦部分很难找到它再原图中的位置
* 边缘相比平坦要号召一些,但也不能一下确定
* 角点可以一下就能找到其再原图中的位置

什么是特征

图像特征就是指有意义的图像区域,具有独特性,易于辨识性,比如角点,,斑点 以及高密度区

角点:
* 灰度梯度的最大值对应的像素
* 两条线的交点
* 极值点(一阶导数最大值,但二阶导数为0)

Harris角点
('./public/images/028-Harris.png')

* 光滑地区,无论向哪里移动,衡量系数不变
* 边缘地址,垂直边缘移动时,衡量系统变化剧烈
* 在焦点处,无论往哪个方向移动,衡量系统都变化剧烈
API
cornelHarris(img,blockSize,ksize,k)
blockSize:检测窗口大小
ksize:SObel卷积核
k:权重系系数,经验值,一般取0.02-0.01
返回值:
所有角点

'''
import cv2
import numpy as np

img = cv2.imread('./public/images/zheng.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
harris = cv2.cornerHarris(gray, blockSize=2, ksize=3, k=0.04)

# img[harris > 0.01*harris.max()] = [0, 0, 255]
# cv2.imshow('img', img)
# ('./public/images/028-Harris效果图.png')

'''
Shi-Tomasi角点
* 对Harris角点加测的改进
* Harris 检测算的稳定性和K值有关,而K是个经验值,不好设定值
API
goodFeaturesToTrack(img,maxCorners,...)
maxCorners:角点的最大数,值为0 表示无限制
qualityLevel: 小于1.0的证书,一般在0.01-0.1之间
minDistance: 角之间最小欧式举例,忽略小于此距离的点
mask: 感兴趣的区域
blockSIze:检测窗口的大小
useHarrisDetector: 是否使用Harris算法,默认值为false,
k:默认是0.04

'''

corners = cv2.goodFeaturesToTrack(
    gray, maxCorners=1000, qualityLevel=0.01, minDistance=10)

corners = np.int0(corners)
for i in corners:
    # 多维转一维
    x, y = i.ravel()
    cv2.circle(img, (x, y), 3, (255, 255, 255), -1)

cv2.imshow('shi', img)


cv2.waitKey(0)

特征检测-SIFT

# SIFT 关键点检测
'''
Scale-Invariant Feature Transform
与缩放无关的关键点检测

SIFT出现的原因
* Harris 角点具有旋转不变的特性
* 但缩放后就可能不再是关键点了

使用SIFT 步骤
创建SIFT对象
进行检测 ,kp=sift.detect(img,...)
绘制关键点,cv2.drawKeypoints(gray,kp,img)


'''

import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)


sift = cv2.SIFT_create()

# 第二个参数none代表整个区域检测
kp = sift.detect(gray, None)

cv2.drawKeypoints(gray, kp, img)

'''
计算SIFT 描述子

描述子和关键点的区别
关键点:位置,大小和方向
关键点描述子:记录了关键点周围对其有贡献的像素点的一组向量值,其不受仿射变换,光照变换影响

API:

des: 描述子 ,其作用是进行特征匹配
kp: 关键点 keypoints

kp, des = sift.compute(img,kp)

同时计算关键点和描述
kp ,des = sift.detectAndCompute(img,...)
mask:指明对img中哪个区域进行计算
'''
kp2, des2 = sift.detectAndCompute(gray, None)
print(des2)


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

特征检测-SURF

# SURF特征检测
'''
Speeded-UP Robust Features
加速的鲁棒性
SIFT最大的问题是速度慢,因此才有SURF

使用SURF 的步骤

surf = cv2.xfeatures2d.SURF_create()
kp ,des = surf.detectAndCompute(img,mask)

'''

import cv2
import numpy as np

img = cv2.imread('./public/images/zheng.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 没有该接口此视频实战忽略.如有需要更换为SIFT接口

cv2.SURF

# 第二个参数none代表整个区域检测
kp = sift.detect(gray, None)

cv2.drawKeypoints(gray, kp, img)

特征检测-ORB

# ORB 特征检测
'''
Oriented Fast And Rotated Brief
特征点检测与描述子

ORB优势:
ORB 可以做到实时检测

FAST特征点检测: 可以做到特征点的实时检测
brief:是对已检测到的特征点进行描述,它加快了特征描述符建立的速读,同时也极大降低了计算的速读

使用ORB步骤

orb = cv2.ORB_create()





'''

'''
总结:
SIFT :计算准确率最高,速读最慢,能检测出来的关键点和描述子最多欧

SURF:速读比SIFT,计算准确率比SIFT低
ORB:速读最快,能做到实时性,计算准确率最低 

'''


import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 没有该接口此视频实战忽略.如有需要更换为SIFT接口

sift = cv2.ORB_create()

# 第二个参数none代表整个区域检测
kp, des = sift.detectAndCompute(gray, None)

cv2.drawKeypoints(gray, kp, img)

cv2.imshow('img', img)

cv2.waitKey(0)

特征匹配方法

# 特征匹配方法
'''
BF: Brute-Force 暴力特征匹配方法
FLANN  最快邻近特征匹配方法

暴力特征匹配原理:它使用第一组中的每个特征的描述子,与第二组中的所有特征描述子进行匹配(枚举匹配) 计算他们之间的差距,然后将最近一个匹配返回

OpenCV特征匹配步骤

创建匹配器 BFMatcher  (normType,crossCheck )

normType 近似度算法 ,有以下几种
* NORM_L1  用于SIFT 和SURF
* NORM_L2 用于SIFT 和SURF ,默认值
* HAMMING1 用于ORB 算法
* HAMMING2 用于ORB算法

crossCheck 是否进行交叉匹配,默认为false ,开启后计算量会更大



进行特征匹配   bf.match (des1 ,des2 )
des:描述子

回执匹配点:cv2.drawMatches(img1,kp1,img2,kp2)

搜索图 img1 ,kp1
匹配图 img2  kp2

match() 方法返回匹配的结果


'''

import cv2
img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')

# 灰度化
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# 创建sift
sift = cv2.SIFT_create()

kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)

bf = cv2.BFMatcher(cv2.NORM_L2)

match = bf.match(des1, des2)

img3 = cv2.drawMatches(img1, kp1, img2, kp2, match, None)

cv2.imshow('img3', img3)

('./public/images/029-暴力特征匹配效果.png')


cv2.waitKey(0)

特征匹配-FLANN

# FLANN 特征匹配

'''
在进行批量特征匹配时,FLANN速读更快
由于它使用的邻近近似值,所以精度较差

使用步骤

* 创建FLANN匹配器,FlannBasedMather(index_params)
    index_params 字典:匹配算法KDTREE, LSH
        kdtree 适用于 SIFT算法 SURF 算法
        lsh  适用于orb算法
    如果是KDTREE 算法,还需要设置 search_params 字典:经验值:一般为index_params 的10倍 
    
    KDTREE : index_params = dict( algorithm = FLANN_INDEX_KDTREE,trees = 5)  FLANN_INDEX_KDTREE 实际就是1
    search_params = dict (checks = 50 )
    

* 进行特征匹配 flann.match/knnMatch()
    knnMatch(param1,param2)
    参数为SIFT SURF ORB 等计算的描述子
    k 表示取欧式举例最近的前k个关键字
    返回的是一个DMatch对象
        distance: 描述子之间的举例,值越低越好
        queryIdx 第一个图像的描述子索引值
        trainIdx 第二个图的描述子索引值
        imgIdx 第二图的索引值

* 绘制匹配点 cv2.drawMathes/drawMatchesKnn(...)

   drawMathesKnn
      搜索img ,kp
      匹配图像img ,kp
      match()方法返回的匹配结果
      

'''


import cv2
import numpy as np


# 打开两个文件
img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')

# 创建特征检测器

sift = cv2.SIFT_create()

# 灰度化
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)


kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)

# 创建匹配器
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)

# 对描述子进行匹配计算
matches = flann.knnMatch(des1, des2, k=2)

good = []
for i, (m, n) in enumerate(matches):
    # 越小,近似度越高
    if m.distance < 0.7 * n.distance:
        good.append(m)


ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)

cv2.imshow('re', ret)
cv2.waitKey(0)

图像查找

# 图像查找
'''
特征匹配+ 单应性矩阵

单应性矩阵 :透视变换
'''

# 打开两个文件
import cv2
import numpy as np


img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')

# 创建特征检测器

sift = cv2.SIFT_create()

# 灰度化
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)


kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)

# 创建匹配器
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)

# 对描述子进行匹配计算
matches = flann.knnMatch(des1, des2, k=2)

good = []
for i, (m, n) in enumerate(matches):
    # 越小,近似度越高
    if m.distance < 0.7 * n.distance:
        good.append(m)


if len(good) >= 4:
    srcPts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
    dstPts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

    # 获取单应性矩阵
    H, _ = cv2.findHomography(srcPts, dstPts, cv2.RANSAC, 5.0)
    h, w = img1.shape[:2]
    pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]
                     ).reshape(-1, 1, 2)
    dst = cv2.perspectiveTransform(pts, H)
    cv2.polylines(img2, [np.int32(dst)], True, (0, 0, 255))
else:
    print('eror')
    exit()

ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)

cv2.imshow('re', ret)
cv2.waitKey(0)

图像合并

# 图像合并的步骤
'''
读文件并重置尺寸
根据特征点和计算描述子,得到单应性矩阵
图像变换
图像拼接并输出图像
'''

'''
坐标系
此坐标与之前的坐标不同,

('./public/images/034-坐标系.png)

('./public/images/035-图像拼接原图.png)
('./public/images/036-图像拼接结果.png)
('./public/images/037-单应性矩阵变换.png)
('./public/images/038-单应性矩阵变换.png)
('./public/images/039-放大窗口.png)
('./public/images/040-图像平移.png)
('./public/images/041-图像平移.png)

'''

# 找特征点,描述子,计算单应性矩阵




import cv2
import numpy as np
def get_home(img1, img2):
    # 创建特征转换对象
    sift = cv2.SIFT_create()
    # 通过特征点转换对象获得特征点和描述子
    k1, d1 = sift.detectAndCompute(img1, None)
    k2, d2 = sift.detectAndCompute(img2, None)
    # 创建特征匹配器
    bf = cv2.BFMatcher()
    # 进行特征匹配
    # k 值,代表选取最匹配的k个点
    match = bf.knnMatch(d1, d2, 2)
    # 过滤特征,找出有效的特征匹配点,过滤阈值 0.7 或 0.8

    verify_ratio = 0.8
    # 有效特征点
    verify_matches = []
    for m1, m2 in match:
        # 如果之间的距离 小于 0.8 说明准确率越高.大于0.8 ,说明准确率越低
        if m1.distance < 0.8 * m2.distance:
            verify_matches.append(m1)
    # 计算单应性矩阵
    # 进行单应性矩阵 的有效特征点必须 大于 8个点.
    min_matches = 8

    if len(verify_matches) > min_matches:
        # img1 坐标点 和img2 的坐标点
        img1_pts = []
        img2_pts = []
        for m in verify_matches:
            # 添加第一张图片的特征点对应的图标
            img1_pts.append(k1[m.queryIdx].pt)
            img2_pts.append(k2[m.queryIdx].pt)
            # 对坐标点进行重新定义,更换格式符合计算单应性矩阵的格式
            # [(x1,y1),(x2,y2),...]
            # [[x1,y1],[x2,y2],...]
            # x 是
        img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
        img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)

        H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
        return H
    else:
        print('error : 没有足够的特征匹配点')
        exit()

# 图像拼接


def stitch_image(img1, img2, H):
    # 获得每张图片的四个角点 逆时针
    # 获得高度,宽度
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
    # 转换为浮点型,转为3维
    # 第一个值 x  设置为 -1 ,任意值
    # 第二个值 y  有一列
    # 第三个值  每个y 有2个值,
    img1_dims = np.float32(
        [[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
    img2_dims = np.float32(
        [[0, 0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)
    # 对图片1进行变换,目的是有角度的拼接,类似于投影变换
    # img1_transform  如果为负值 ,则代表不显示
    img1_transform = cv2.perspectiveTransform(img1_dims, H)
    # 将图形1 和图形2 进行拼接    axis 0 横向拼接,  .
    result_dims = np.concatenate((img2_dims, img1_transform), axis=0)
    # 计算x ,y最大值和最小值
    # 二维变成一维 ,四舍五入
    [x_min, y_min] = np.int32(result_dims.min(axis=0).ravel()-0.5)
    [x_max, y_max] = np.int32(result_dims.min(axis=0).ravel()+0.5)

    # 得到平移后的举例
    transform_dist = [-x_min, -y_min]

    # 平移后的图片
    # ('./public/images/042-对图1进行平移.png')
   #  result_img = cv2.warpPerspective(img1, H, (x_max - x_min, y_max - y_min))

    # 将图2 平移过来
    # 线性代数 矩阵平移 乘以 起始坐标 起始坐标如下.
    # [1,0,dx]
    # [0,1,dy]
    # [0,0,1]
    transform_array = np.array(
        [
            [1, 0, transform_dist[0]],
            [0, 1, transform_dist[1]],
            [0, 0, 1]
        ]
    )
    result_img = cv2.warpPerspective(
        img1, transform_array.dot(H), (x_max - x_min, y_max - y_min))

    # 拼接
    result_img[transform_dist[1]:transform_dist[1]+h2,
               transform_dist[0]:transform_dist[0]+w2] = img2

    return result_img

    # 读取文件,设置成固定的大小
img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')

img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

# 横向拼接图片
inputs = np.hstack((img1, img2))

# 找特征点,描述子,计算单应性矩阵,此功能比较单一,写成函数

H = get_home(img1, img2)

# 根据单应性矩阵对图像进行变换,然后平移到大的图像,再平移

result_img = stitch_image(img1, img2, H)


# 拼接并输出最终结果
cv2.imshow('img', inputs)

cv2.waitKey(0)

图像分割

# 图像分割

# 将前景物体从背景中分离出来

# 传统的图像分割方法

# 分水岭法
#   GrabCut法-ps抠图
#   MeanShift法
#   背景扣除
# 基于深度学习的图像分割方法


# 分水岭法
# 图像经过二值化后 纵轴表示 0 - 255 0 .黑点 255 白点
'''
('./public/images/045-分水岭法1.png')
('./public/images/046-分水岭法2.png')
('./public/images/047-分水岭法.png')

分水岭法的处理步骤
 * 标记背景
 * 标记前景
 * 标记未知区域
 * 进行分割

'''

# 实例: 分割硬币

'''
分水岭API

watershed(img,masker)
masker:包括前景标记,背景标记,未知区域

获取背景:
('./public/images/048-分割硬币-获取背景.png')
('./public/images/049-分割硬币-获取前景.png')
('./public/images/050-分割硬币-未知区域.png')


'''


# 获取背景
# 1. 通过二值法得到黑白图片
# 2. 通过形态学获取背景


import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('./public/images/coins.webp')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 0的位置   如果设置为100 最小值 超过100设置为白色,小于100 为黑色  白色为255,则不用 在  cv2.THRESH_BINARY  + cv2.THRESH_OTSU
# ret:执行结果成功与否 thresh:成功的图片
# thresh_otsu 自适应阈值,会将100设置为自适应的阈值.所以100的位置设置为0
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 对硬币进行形态学处理,保证硬币是硬币,背景是背景
# 去除外部白色噪点

kernel = np.ones((3, 3), np.int8)
# 2是进行开运算两次
open1 = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, 2)

# 膨胀 获取背景物体
bg = cv2.dilate(open1, kernel, iterations=1)

# 距离变换API -计算非0值到离他最近的0值的距离,
#  distanceTransform(img,distanceType,maskSize)
#  distanceType: DIST_L1 、DIST_L2 ,一般用L2 ,即用勾股定理
#  (x1,y1) 非0值的坐标  (x2,y2) 0值的坐标
# L1 绝对值的计算  (|x1| - |x2|) + (|y1| - |y2|)
# L2   勾股定理
# maskSize 扫描的时候卷积核的大小,一般L1 用3 大小的卷积核  L2用 5 大小 的卷积核

# 连通域API
# 连通区域(Connected Component)一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域(Region,Blob)。
# 连通区域分析(Connected Component Analysis,Connected Component Labeling)是指将图像中的各个连通区域找出并标记。
# 连通区域分析是一种在CVPR和图像分析处理的众多应用领域中较为常用和基本的方法。
# 例如:OCR识别中字符分割提取(车牌识别、文本识别、字幕识别等)、视觉跟踪中的运动前景目标分割与提取(行人入侵检测、遗留物体检测、基于视觉的车辆检测与跟踪等)、
#            医学图像处理(感兴趣目标区域提取)、等等。也就是说,在需要将前景目标提取出来以便后续进行处理的应用场景中都能够用到连通区域分析方法,通常连通区域分析处理的对象是一张二值化后的图像。
# https://zhuanlan.zhihu.com/p/44702120
# connectedComponents(img,connectivity,...)
# 求图像中所有非0的连通域
# connectivity :4 8 (默认)
#  connectivity 个角点进行判断
#  4 :非0值的上下左右的四个点, 8 :非0值的上下左右以及四个角点


# cv2.imshow('thresh', thresh)

# cv2.imshow('bg', bg)

# 获取前景图

dist = cv2.distanceTransform(open1, cv2.DIST_L2, 5)

# imshow 显示梯度的时候效果不是很好,引用  matplotlib
# ('./public/images/051-连通域效果.png')
# cv2.imshow('dis', dist)
# plt.imshow(dist, cmap='gray',)
# plt.show()

ret2,  fg = cv2.threshold(dist, 0.7 * dist.max(), 255, 0)
# ('./public/images/052-分水岭法获取前景效果图.png')
# cv2.imshow('fg', fg)

# 获取未知区域  未知区域  = 背景 - 前景
unknow = cv2.subtract(bg, np.uint8(fg))
# cv2.imshow('unknow', unknow)

# 创建连通域
ret, marker = cv2.connectedComponents(np.uint8(fg))
#  将背景和位置区域都设置好 用1 代表 背景,对所有元素 +1 背景也+1 了.前景在原来的基础上+1 对前景没有区别
marker = marker + 1
# 未知区域全部为0
marker[unknow == 255] = 0


# 进行图像分割,所有的边缘用-1表示

result = cv2.watershed(img, marker)
# 将边缘设置为红色
img[result == -1] = [0, 0, 255]

('./public/images/053-分水岭分割最终效果图.png')
cv2.imshow('img', result)

cv2.waitKey()

图像分割-前后景-GrabCut

# GrabCut 法- 前后景分离
'''
通过交互的方式(鼠标)获取前景物体.

* 用户指定前景的大体区域,剩下的为背景区域.
* 用户还可以明确指定某些地方为前景或背景
* GrabCut采用分段迭代的方法分析前景物体形成模型树
* 最后根据权重决定某个像素是前景还是背景

[示例]GrabCut

* 主体结构
* 鼠标事件的处理
* 调用GrabCut实现前景和背景的分离

GrabCut API 
grabCut(img,,mask,bgdModel,fgdModel,iterrator,mode)

mask: 进行分割之后产生的掩码是多少.得到掩码后,就能将要分割的图片摘取出来.每一个像素值的含义都能了解到
rect: 图片选取的区域
bgdModel:固定值
fgdModel:固定值
iterator ;迭代次数

mask:
BGD 背景 0
FGD 前景  1
PR_BGD 可能是背景 2
PR_FGD 可能是背景 3 

bgdModel  是 np.float64 type zero arrays of size(1,65) 64位的浮点型
fgdModel 同上

mode

GC_INIT_WITH_RECT : 指定区域找前景
GC_INIT_WITH_MASK :第一次调用  GC_INIT_WITH_RECT 对前后景进行分离,之后还可以通过  GC_INIT_WITH_MASK 进行迭代

'''

import cv2

import numpy as np

# 定义一个类,用户对象的数据共享


class App:

    startX = 0
    startY = 0
    rect = (0, 0, 0, 0)

    flag_rect = False

    def onmouse(self, event, x, y, flags, param):

        if event == cv2.EVENT_LBUTTONDOWN:
            self.flag_rect = True
            self.startX = x
            self.startY = y
            print('左键下')
        elif event == cv2.EVENT_LBUTTONUP:
            cv2.rectangle(self.img, (self.startX, self.startY),
                          (x, y), (255, 255, 255), 3)
            self.flag_rect = False
            self.rect = (min(self.startX, x), min(self.startY, y),
                         abs(self.startX - x), abs(self.startY - y))
            print('左键上')
        elif event == cv2.EVENT_MOUSEMOVE:
            if self.flag_rect == True:
                # 关键代码,每次都是拿一个新的img进行绘制
                self.img = self.img2.copy()
                cv2.rectangle(self.img, (self.startX, self.startY),
                              (x, y), (0, 255, 0), 3)
            print('鼠标挪动')

    def run(self):
        print('self')
        cv2.namedWindow('input')
        cv2.setMouseCallback('input', self.onmouse)

        self.img = cv2.imread('./public/images/youke.jpg')
        self.img2 = self.img.copy()
        # 掩码只有一层,所有只需要shape的前两个参数
        self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)
        self.output = np.zeros(self.img.shape, np.uint8)

        while (1):
            cv2.imshow('input', self.img)
            cv2.imshow('self', self.output)
            k = cv2.waitKey(100)
            if k == 27:
                break
            if k == ord('g'):
                bgdModel = np.zeros((1, 65), np.float64)
                fgdModel = np.zeros((1, 65), np.float64)
                cv2.grabCut(self.img2, self.mask,
                            self.rect, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_RECT)
                mask2 = np.where((self.mask == 1) | (
                    self.mask == 3), 255, 0).astype('uint8')
                self.output = cv2.bitwise_and(self.img2, self.img2, mask=mask2)


App().run()

# 画框之后摁 g

图像分割-MeanShift

'''
MeanShift 原理
严格来说该方法并不是用来对图像分割的,而是在色彩层面的平滑滤波
它会中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的颜色区域
它以图像上任一点P为圆心,半径为sp ,色彩幅值 sr 进行不断的迭代
sr 越大     对判断颜色相同的差异要求越低,准确率越低
sr 越小     对判断颜色相同的差异要求越高,准确率越高

('./public/images/054-图像分割-MeanShift 效果图.png')

API

pyrMeanShiftFiltering(img,double sp ,double sr ,maxLevel = 1)
maxLevel 默认值





'''

import cv2
import numpy as np

# img = cv2.imread('./public/images/youke.jpg')
img = cv2.imread('./public/images/coins.webp')


sp = 20

sr = 30

mean_img = cv2.pyrMeanShiftFiltering(img, sp, sr, 1)

# 之前有介绍过的Canny算法,寻找边缘
img_canny = cv2.Canny(mean_img, 150, 300)

# 分割出来的所有区域,第二个参数为所有区域的层级关系
contours, _ = cv2.findContours(
    img_canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(img, contours, -1, (255, 255, 255), 2)

cv2.imshow('img', img)

cv2.imshow('mean', mean_img)

cv2.imshow('imgc', img_canny)

cv2.waitKey()

视频前后景分离

'''
视频前后背景分离

视频背景扣除原理

* 视频是一组连续的帧(一幅幅图组成)
* 帧与帧之间关系密切,组成一组帧,称为 GOP
* 在GOP中,背景几乎是不变的

MOG去除背景
* 混合高斯模型为基础的前景/背景分割算法
* createBackgroundSubtractorMOG(
    history,//默认200ms 建立参考帧至少需要200ms
    nmixtures,//高斯范围值,默认为5 像素,把一个大的图片分成5x5的小块,每个5x5的小块都有一个高斯值,从而算出参考模型
    backgroundRatio,//背景比率,默认0.7 这张图中 70% 属于背景.
    nosiseSigma //默认0 自动降噪 
    
)

'''

import cv2
import numpy

# 从设备号中获取
cap = cv2.VideoCapture(0)

mog = cv2.createBackgroundSubtractorMOG2()


while (True):
    # ret: 结果 frame:一帧一帧的页面
    ret, frame = cap.read()
    # 掩码
    fgmask = mog.apply(frame)

    cv2.imshow('img2', frame)
    cv2.imshow('img', fgmask)

    k = cv2.waitKey(1)

    if k == 27:
        break

cap.release()
cv2.destroyAllWindows

视频前后景分离-MOG2

'''
MOG2去背景
同MOG 类似,不过对亮度产生的阴影有更好的识别,缺点,产生噪点
API
createBackgroundSubtractorMOG2(
history,//500毫秒
...

detectShadows//是否检测阴影,默认为True

GMG去背景 
静态背景图像估计和每个像素的贝叶斯分割抗噪性更强
优点;可以计算阴影背景,去除噪点
缺点:采用默认值,则在开始好长时间内是黑屏,取决于帧率.

API:
cv2.createBackgroundSubtractorGMG(

initializationFrames ,//初始帧数,120,这和MOG MOG2 最大的区别

)

)
'''

import cv2
import numpy

# 从设备号中获取
cap = cv2.VideoCapture(0)

# mog = cv2.createBackgroundSubtractorMOG2()

mog = cv2.bgsegm.createBackgroundSubtractorGMG()


while (True):
    # ret: 结果 frame:一帧一帧的页面
    ret, frame = cap.read()
    # 掩码
    fgmask = mog.apply(frame)

    cv2.imshow('img2', frame)
    cv2.imshow('img', fgmask)

    k = cv2.waitKey(1)

    if k == 27:
        break

cap.release()
cv2.destroyAllWindows

图像修复

'''
图像修复

('./public/images/055-图像修复效果.png')

API

inpaint(
img,
mask,//与原始图片一样大小的,黑底白色的残缺位置的图片
inpaintRadius,//每个点的圆形领域半径
flags //INPAINT_NS INPAINT_TELEA
)

INPAINT_NS 


INPAINT_TELEA 算法简单,破损里的平方加权平均值

# 有点不是很智能,需要找到破损的图片
'''

import cv2

import numpy as np

img = cv2.imread('./public/images/coins.webp')

mask = cv2.imread('./public/images/coins.webp', 0)

dst = cv2.inpaint(img, mask, 5, cv2.INPAINT_TELEA)

cv2.imshow('dst', dst)
cv2.waitKey()

人脸检测-Harr

'''
基本概念:
范围,互为包含关系
人工智能 > 机器学习 > 深度学习

计算机视觉与机器学习的关系

计算机视觉是机器学习的一种医用,而且是最有价值的应用

人脸识别

有两种主要的方法

哈儿级联方法(Harr) --cv自带的
深度学习方法(DNN)


哈儿级联法 

Harr是专门解决人脸识别而退出的
在深度学习还不流行的时候,Harr已可以商用.

Harr人脸识别步骤

创建Harr级联器
导入图片并将其灰度化
调用detectMultiScale方法进行人脸识别

detectMultiScale(img,
double scaleFactor =1.1  //缩放因子,哈儿级联器,对图片进行扫描,可能扫描的时候不能把人脸扫描进来,对图片进行缩小或进行放大.
int minNeighbors = 3  //最小的像素值,人脸识别的时候最少需要的像素
...

)

优缺点: 可以检测到正面的图片,但是侧面检测不到
'''

import cv2

import numpy as np

# 创建harr级联器

# 这里需要输入已经训练好的训练模型,需要识别脸的话就输入脸的模型,眼睛的的话就输入眼睛的模型
facer = cv2.CascadeClassifier()

# 导入人脸识别图片并将其灰度化
img = cv2.imread('')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 第三步,进行人脸识别
# [ [x,y,w,h]  ]
facer.detectMultiScale(gray, 1.1, 5)

for (x, y, w, h) in facer:
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)

cv2.imshow('img', img)

cv2.waitKey()

车牌识别-Harr+Tesseract

'''
Harr+Tesseract 车牌识别

Harr能定位到车牌的大体位置.
tesseract 提取数字


]步骤
通过Harr定位车牌的大体位置
对车牌进行预处理
调用tesseract进行文字识别

车牌预处理包括的内容
对车牌进行二值化处理
进行形态学处理
滤波去除噪点
缩放

Tesseract的安装

(https://blog.csdn.net/LOVEmy134611/article/details/119344846)



'''

import cv2

import numpy as np

import pytesseract as py

plate = cv2.CascadeClassifier('')

# 带车牌的图片
img = cv2.imread('./')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 检测车牌的位置

faces = plate.detectMultiScale(gray, 1.1, 3)

for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 255), 2)


# 对获取到的车牌进行预处理,

# 提取ROI

roi = gray[y:y+h, x:x+w]

# 进行二值化

ret, roi_bin = cv2.threshold(roi, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# 多个语言时,中间用加号

result = py.image_to_string(roi, lang='chi_sim+eng', config='--psm 8 --oem 3')

print(result)
cv2.imshow('roi_bin', roi_bin)

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

进阶课程一些概念

import cv2
from cv2 import dnn
import numpy as np
'''

* 什么是深度学习
* 几种常见的深度学习网络
* 构建深度学习网络的框架tf ,pytorch
* 进行网络训练的数据集

深度学习基础概念
深度学习是计算机视觉最为重要的方法

* 深度学习在40年代就被剔除,但一直停滞不前
* 2011,微软在语音识别上使用,取得了突破的效果
* 2012 ,DNN在图像识别领域取得惊人的效果,错误率从26% 降低到15%
* 2016 AlphaGo击败人类,引起世界震惊

深度学习网络模型

DNN(Deep Neural Network) 深度神经网络

RNN CNN 是属于一种特殊的DNN 

RNN(Recurrent Neural Network) 循环神经网络
CNN(Convolutional Neural Network) 卷积神经网络

RNN用途
* 语音识别
* 机器翻译
* 生成图像描述

CNN:
* 图片分类,检索
* 目标定位检测
* 目标分割
* 人脸识别

几种CNN网络实现
* LeNet 1998 第一代 28x28手写
* AlexNet 2012 ImageNet 比赛的冠军
* 还有这几种: VGG ,GoogleLeNet , ResNet

几种CNN目标检测网络实现
* RCNN ,FastRCNN ,Faster RCNN
* SSD
* YOLO  YOLOv2 ...  YOLOv5

深度学习库
* tensorflow ,google出品
* caffe -> caffe2 ->torch(pytorch)  贾扬清开源 ->facebook 提供
* MXNet Apache出品

训练数据集 -> 只有经过数据集训练 神经网络 才能成效

* MNIST Fashion-MNIST 手写字母
* VOC 举办挑战赛时的数据集 2012年后欧不再举办
* COCO 用于目标检测的大型数据集
*  ImageNet 

通过网络和数据集训练出来的模型称为训练模型

TF训练出的模型是 *.pb 文件
Pytorch 训练出的模型是 *.pth 文件
Caffe 训练处的模型是 .caff
ONNX 开放性神经网络交换格式 .onnx 

OpenCV 对DNN的支持



OpenCV 3.3 将DNN转正
OpenCV 只能使用DNN 不能训练DNN模型

OpenCV 支持的模型
* Tensorflow
* pytorch/torch
* caffe
* darknet


'''

'''
OpenCV使用DNN
DNN使用步骤

1. 读取模型,并得到深度神经网络
2. 读取图片、视频我
3. 将图片转成张量,送入深度神经网络
4. 进行分析,并得到结果

'''
'''
导入模型API

readNetFromTensorflow
readNetFromCaffe
readNetDarknet,YOLO
readNet -- 会根据不同的模型读取不同的网络

导入模型API参数

readNetFromTensorflow(model,config)
readNetFromCaffe/Darknet(config,model)
readNetFrom(model,[config,[framework]])

读取图片并转换为张量

API

blobFromImage(
image,
scalefactor =1.0,//图像缩放比例
size=Size(),//图像大小
mean=Scalar(), //是一个RGB的三元组,作用是消除图像中光照的变化,给定的RGB 均值是103 116 123 
swapRB=false, //图像中的RB是否交换,对于OpenCV 来说图像是RGB 对于深度学习的模块来说可能是RGB 也可能BGR 默认不交换
crop = false ... //是否对图片进行裁剪。超出模型的大小是否裁剪,默认是不裁剪

)

mean的含义

('./public/images/059-mean的含义.png')
主要减少光照的影响


将张量送入网络并执行

net.setInput(blob)
net.forward()

[实战,物体分类]

'''


# from cv2 import dnn_ClassificationModel as dc
# 1.导入模型,创建神经网络
# 2.读取图片,转成张量
# 3.将张量输入到网络中,并进行预测
# 4.得到结果进行显示

config = ''
model = ''

net = dnn.readNetFromCaffe(config, model)
img = cv2.imread('./public/images/coins.webp')
blob = dnn.blogFromImage(img, 1.0, (224, 244), (104, 117, 123))
net.setInput(blob)
r = net.forward()  # r是人类不可读的

# 读入类别目录

classes = []
path = ''
with open(path, 'rt') as f:
    classes = [x[x.find(" ")+1:] for x in f]


order = sorted(r, reverse=True)

z = list(range(3))
for i in range(0, 3):
    z[i] = np.where(r[i] == order[i][0][0])
    print('第', i+1, '项匹配', classes[z[i]], end='')
    print('类所在行:', z[i]+1, '可能性', order[i])