opencv最全笔记(每个函数包含一个demo)

文章目录


openCV:

  1. 安装Anaconda:

  2. 之后安装opencv

    pip install opencv-python opencv-contrib-python jupyter matplotlib -i https://pypi.douban.com/simple
    Looking in indexes: https://pypi.douban.com/simple
    

创建和显示窗口:

●namedWindow() 创建命名窗口
●imshow() 显示窗口
●destroyAllwindws() 摧毁窗口
●resizeWindow() 改变窗口大小
●waitKey() 等待用户输入

import cv2

#创建窗口
cv2.namedWindow('window',cv2.WINDOW_NORMAL) # WINDOW_AUTOSIZE

#更改窗口的大小  创建的窗口的模式必须是  WINDOW_NORMAL
cv2.resizeWindow('window',800,600)
#展示名字为window的窗口
cv2.imshow('window',0)
#等待按键
key = cv2.waitKey(0)  #0 表示接受任意案件   如果给的是其他的整数表示等待按键的时间,单位是毫秒 
if key & 0xFF == ord('q'):
    print('准备销毁窗口')
    cv2.destroyAllWindows()  #销毁窗口函数

显示图片:

import matplotlib.pyplot as plt


#默认图片是彩色的 
cat =cv2.imread('./img1.png')
cat

plt.imshow(cat)  #显示图片  发现显示的图片和真实的图片一样  
# 因为opencv 读进来的图片的通道不是RGB 而是按照BGR (蓝绿红)(进行排列的)  
# 所以opencv 读进来的图片不要用别的方式去展示比如 matplotlib

在这里插入图片描述

使用opencv 自己的方法进行展示图片:

import cv2
#使用opencv 的方式来显示图片
cat =cv2.imread('./img1.png')  #读取图片
cv2.imshow('cat',cat)  #第一个参数表示窗口的名称  第二个参数表示我们要显示的图片


#等待按键
key = cv2.waitKey(0)  # 0 表示接受任意案件   如果给的是其他的整数表示等待按键的时间,单位是毫秒 
if key & 0xFF == ord('q'):
    print('准备销毁窗口')
    cv2.destroyAllWindows()  #销毁窗口函数

在这里插入图片描述

把显示图片的代码封装为一个函数:

def cv_show(name,img):
	cv2.imshow(name,img)
	key=cv2.waitKey(0)
	if key & 0xFF == ord('q'):
		cv2.destroyAllWindows()

保存图片:

import cv2

#创建一个窗口
cv2.namedWindow('img')
cv2.resizeWindow('img',640,480)


img =cv2.imread('./img1.png')  #读取图片


while True: 
    cv2.imshow('cat',img)  #第一个参数表示窗口的名称  第二个参数表示我们要显示的图片
    key = cv2.waitKey(0)
    
    if key == ord('q'):  #如果按下的是q 键就退出
        break
    elif key  == ord('s'):
        cv2.imwrite('./123.png',img) # 如果按下的是一个s 按键就继续宁保存文件到本地
    else:
        print(key)
cv2.destroyAllWindows() #摧毁窗口

读取摄像头和读取视频数据:

读取摄像头数据:
#打开摄像头
import cv2
#创建一个窗口
cv2.namedWindow('video',cv2.WINDOW_NORMAL)
cv2.resizeWindow('video',640,480) #设置大小

# 如果没有读取到摄像头就不会报错
cap=cv2.VideoCapture(0)
#循环读取摄像头 的每一帧
while True:
    #读取每一帧数据, 返回标记和这一帧数据 True 表示读取到了数据 False表示没有读取到数据
    ret, frame = cap.read()
    
    #可以根据ret 进行判断
    if not ret:
        #没有读取到数据,直接推出
        break
    #显示数据
    cv2.imshow('video',frame)
    
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
        
#别忘记释放资源
cap.release()
cv2.destroyAllWindows()
    
读取视频:
#打开摄像头
import cv2
#创建一个窗口
cv2.namedWindow('video',cv2.WINDOW_NORMAL)
cv2.resizeWindow('video',640,480) #设置大小

#打开视频
cap = cv2.VideoCapture('./My Application – MainActivity.java [My_Application.myapplication6.main] 2022-11-05 13-36-10.mp4')
#循环读取摄像头 的每一帧
while True:
    #读取每一帧数据, 返回标记和这一帧数据 True 表示读取到了数据 False表示没有读取到数据
    ret, frame = cap.read()
    
    #可以根据ret 进行判断
    if not ret:
        #没有读取到数据,直接推出
        break
    #显示数据
    cv2.imshow('video',frame)
    # 只能是整数
    key = cv2.waitKey(10)  #   1000//30  表示整除
    if key == ord('q'):
        break
        
#别忘记释放资源
cap.release()
cv2.destroyAllWindows()
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8p8IO0pQ-1690355241619)(openCV.assets/image-20230316212332356.png)]

录制视屏:

import cv2

cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')

# 创建videoWriter
vw = cv2.VideoWriter('output.mp4', fourcc, 30, (640, 480))

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    # 写每一帧
    vw.write(frame)
    cv2.imshow('frame', frame)

# cv2.waitKey(1) 表示一毫秒就是一张图片 太快了 可以适当调低一点
    if cv2.waitKey(1) == ord('q'):
        break

# 别忘记释放资源
cap.release()
vw.release()
cv2.destroyAllWindows()

控制鼠标:

OpenCV允许我们对窗口上的鼠标动作做出响应.
●setMouseCallback(winname, callback, userdata) winname是窗口的名字, callback是回调函数, userdata是
给回调函数的参数.
●callback(event, x, y, flags, userdata)回调函数必须包含这5个参数. event是事件(鼠标移动,左键,右键x,y是点鼠标的坐标点, flags主要用于组合键, userdata就是上面的setMouseCallback的userdata

扫描二维码关注公众号,回复: 16441270 查看本文章

。EVENT_ MOUSEMOVE 0鼠标移动
。EVENT
LBUTTONDOWN 1按下鼠标左键
。EVENT_ RBUTTONDOWN 2按下鼠标右键
。EVENT
MBUTTONDOWN 3按下鼠标中键
。EVENT_ LBUTTONUP 4左键释放
。EVENT_ RBUTTONUP 5 右键释放
。EVENT_ MBUTTONUP 6中键释放
。EVENT_ LBUTTONDBLCLK 7左键双击
。EVENT_ RBUTTONDBLCLK 8右键双击
。EVENT
MBUTTONDBLCLK 9中键双击
。EVENT_ MOUSEWHEEL 10鼠标滚轮上下滚动
。EVENT
_MOUSEHWHEEL 11鼠标左右滚动

flags: .
。EVENT_ FLAG LBUTTON 1按下左键,
。EVENT_ FLAG RBUTTON 2按下右键
。EVENT_ FLAG MBUTTON 4按下中键
。EVENT_ FLAG CRTLKEY 8按下ctrl键
。EVENT_ FLAG SHIFTKEY 16按下shift键
。EVENT_ FLAG_ ALTKEY 32 按下alt键,

import cv2
import numpy as np

# 函数名可以随便取 , 但是参数必须是5个
#event  表示鼠标事件  x, y 是鼠标的坐标  flags鼠标的组合按键  userdata 传递的用户数据
def mouse_callback(event, x, y, flags, userdata):
    print(event, x, y, flags, userdata)
    
#创建窗口
cv2.namedWindow('mouse',cv2.WINDOW_NORMAL)
#宽度和高度   可以理解为 宽就是列数 高就是行数
cv2.resizeWindow('mouse',640,360)


# 设置鼠标的回调函数
#  mouse 表示窗口   mouse_callback回调函数   123 就是传递进去的数据
cv2.setMouseCallback('mouse',mouse_callback,'123')


#生成、全黑的图片  要和我们设置的窗口的宽度和高度反过来设置  因为相当与 行数和列数
img = np.zeros((360,640,3))
while True:
    cv2.imshow('mouse',img)
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
        
        
cv2.destroyAllWindows()

第四个0 表示没有什么组合鼠标事件 第五个参数就是我们传递进去的数据

在这里插入图片描述

TrackBar控件:

TrackBar控件

在这里插入图片描述

使用trackbar 进行控制 颜色的递增减等效果:(R,G,B)在这里插入图片描述

#trackBar  的使用
import cv2
import numpy as np

#创建窗口
cv2.namedWindow('trackbar',cv2.WINDOW_NORMAL)
cv2.resizeWindow('trackbar',640,480)


#定义回调函数
def callback(value):
    print(value)
    
    #创建三个trackbar
cv2.createTrackbar('R','trackbar',0,255,callback)
cv2.createTrackbar('G','trackbar',0,255,callback)
cv2.createTrackbar('B','trackbar',0,255,callback)

#创建背景图
img = np.zeros((480,640,3),np.uint8)

while True:
    #获取当前trackbar的值
    r = cv2.getTrackbarPos('R','trackbar')
    g = cv2.getTrackbarPos('G','trackbar')
    b = cv2.getTrackbarPos('B','trackbar')
    
    #用获取到的三个之修改背景颜色  img[:] 表示将第三个维度的东西取出来
    img[:] = [b,g,r]  #这里一定要写成 b,g,r 因为opencv中的值就是这样的顺序显示的  所以
    cv2.imshow('trackbar',img) #显示
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
    
cv2.destroyAllWindows()

OpenCV基础知识和绘制图形

2.1 OpenCV的色彩空间
2.1.1 RGB和BGR
最常见的色彩空间就是RGB,人眼也是基于RGB的色彩空间去分辨颜色的.
OpenCV默认使用的是BGR. BGR和RGB色彩空间的区别在于图片在色彩通道.上的排列顺序不同.

在这里插入图片描述

在这里插入图片描述

显示图片的时候需要注意适配图片的色彩空间和显示环境的色彩空间.比如传入的图片是BGR色彩空间,显示环境是RBG空间,就会出现颜色混乱的情况.

HSV, HSL和YUV

2.1.2.1 HSV
●OpenCV用的最多的色彩空间是HSV.
●Hue:色相,即色彩,如红色,蓝色.用角度度量,取值范围为0° ~ 360°,从红色开始按逆时针方向计算,红色为0°,绿色为120*,蓝色为240°
●Saturation:饱和度,表示颜色接近光谱色的程度。- -种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0% ~ 100%,值越大,颜色越饱和。

●Value: 明度.明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0% (黑)到100% (白)。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

HSL在顶部是纯白的,不管是什么颜色.

在这里插入图片描述

HSV和HSL的区别:
HSB和HSL在字面意思上是一样的:
●H指的是色相(Hue) ,就是颜色名称,例如“红色”、“蓝色”;
●S指的是饱和度(Saturation) ,即颜色的纯度;
●L (Lightness) 和B (Brightness) 是明度,颜色的明亮程度
在原理和表现上,HSL和HSB中的H (色泪)完全- 致,但二者的S (饱和度)不-样,L和B
(明度)也不- -样:
●HSB中的S控制纯色中混入白色的量,值越大,白色越少,颜色越纯;
●HSB中的B控制纯色中混入黑色的量,值越大,黑色越少,明度越高
●HSL中的S和黑白没有关系,饱和度不控制颜色中混入黑白的多寡;
●HSL中的L控制纯色中的混入的黑白两种颜色。

2.1.2.3 YUV
YUV,是一种颜色编码方法。常使用在各个视频处理组件中。YUV在对照片 或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
"Y”表示明亮度(Luminance或Luma) ,也就是灰阶值,“U” 和"V”表示的则是色度(Chrominance或
Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
Y’"UV的发明是由于彩色电视与黑白电视的过渡时期。
Y"UV最大的优点在于只需占用极少的带宽。
●4:4:4表示完全取样。
●4:2:2表示2:1的水平取样, 垂直完全采样。
●4:2:0表示2:1的水平取样, 垂直2: 1采样。
●4:1:1表示4:1的水平取样,垂直完全采样。

色彩空间的转换:

#关键API cv2.cvtColor

#关键API cv2.cvtColor
import cv2

def callback(value):
    pass

#创建窗口
cv2.namedWindow('color',cv2.WINDOW_NORMAL)
cv2.resizeWindow('color',640,480)

#读取照片
cat = cv2.imread('./img1.png')

#定义颜色空间命名列表转换
color_spaces = [
    #所有颜色空间转化都是COLOR
    cv2.COLOR_BGR2RGBA,cv2.COLOR_BGR2BGRA,
    cv2.COLOR_BGR2GRAY,cv2.COLOR_BGR2HSV,
    cv2.COLOR_BGR2YUV
]

#设置一个trackbar
cv2.createTrackbar('trackbar','color',0,4,callback) # 0 ,4 表示我们要显示集中颜色  我们这里有物种颜色空间的转换  表示拖动一次就会进行一次转换

while True:
    #获取当前trackbar的值
    index = cv2.getTrackbarPos('trackbar','color')
    
    # 之后进行颜色空间转换
    cvt_img = cv2.cvtColor(cat,color_spaces[index])
    
    cv2.imshow('color',cvt_img) 
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
    
cv2.destroyAllWindows()
    

在这里插入图片描述

mat 的深浅拷贝:

在这里插入图片描述

import cv2
import numpy as np

img = cv2.imread('./img1.png')
#浅拷贝
img2= img.view()

# 深拷贝
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)
cv2.destroyAllWindows()

在这里插入图片描述

颜色通道的分离与合并:

●split(mat)分割图像的通道
●merge((ch1,ch2, ch3))融合多个通道

import cv2 
import numpy as np

img = np.zeros((200,200,3),np.uint8) 

#分割通道
b, g, r = cv2.split(img)

#修改一些颜色  b[10,100,10,100]表示我们在一张图片中进行了一小段的切片 之后更改其颜色 使之有变化
b[10:100,10:100]=255   
g[10:100,10:100] = 255

#合并通道
img2 = cv2.merge((b,g,r))

cv2.imshow('img',np.hstack((b,g))) #b,g 是一维的 所以我们不能在一起去进行堆叠展示
cv2.imshow('img2',np.hstack((img,img2)))  # np.hstack 函数表示将我们的图片进行堆叠展示 就是连载一起

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

绘制图形:

利用OpenCV提供的绘制图形API可以轻松在图像上绘制各种图形,比如直线,矩形,圆,椭圆等图形.
●line(img, pt1, pt2, color, thickness, lineType, shift) 画直线
。img: 在哪个图像上画线
。pt1, pt2:开始点结束点.指定线的开始与结束位置
。color: 颜色
。thickness: 线宽
。lineType: 线型.线型为-1, 4, 8, 16,默认为8
。shift: 坐标缩放比例.
●rectangle0 参数同上画矩形
●circle(img, center, radius, color[, thickness[, lineType[, shift]l)中括号内参数表示可选参数.画圆
●elipse(imng, 中心点长宽的一半,角度,从哪个角度开始,从哪个角度结束…

画直线:
#绘制各种图像

import cv2
import numpy as np


#创建一个纯黑的背景图用来画图形
img = np.zeros((480,640,3),np.uint8)

#line(img, pt1, pt2, color, thickness, lineType, shift)  注意 起始点和颜色等都使用元组来进行书写
cv2.line(img,(10,20),(300,300),(0,0,255),5,4)

cv2.line(img,(80,100),(380,480),(0,0,255),5,16)

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

在这里插入图片描述

绘制矩形和圆形:
#绘制矩形和圆

import cv2
import numpy as np


#创建一个纯黑的背景图用来画图形
img = np.zeros((480,640,3),np.uint8)

#line(img, pt1, pt2, color, thickness, lineType, shift)  注意 起始点和颜色等都使用元组来进行书写
# cv2.rectangle(img,(80,100),(380,480),(0,255,0),5)  #绘制一个矩形

#画圆  需要传递圆心坐标  ,半径大小   颜色等使用元组进行框其来  采用 r,g,b的模式 来展示  255 表示颜色拉满  0 表示颜色为空白
cv2.circle(img,(320,240),50,(0,0,255),5,16)

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

在这里插入图片描述

绘制椭圆:

●elipse(imng, 中心点长宽的一半,角度,从哪个角度开始,从哪个角度结束…

在这里插入图片描述



#绘制椭圆

import cv2
import numpy as np


#创建一个纯黑的背景图用来画图形
img = np.zeros((480,640,3),np.uint8)

# ●elipse(imng, 中心点长宽的一半,角度,从哪个角度开始,从哪个角度结束...
cv2.ellipse(img,(320,240),(100,50),0,0,360,[255,0,0],16)

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

在这里插入图片描述

绘制多变形和填充多边型:

●polylines(img, pts, isClosed, color[, thickness[, lineType[, shif]]) 画多边形
●fillPoly 填充多边形
●putText(img, text, org, fontFace, fontScale, color[, thickness, lineType[, bottomLeftrigin]])绘制

#绘制椭圆

import cv2
import numpy as np


#创建一个纯黑的背景图用来画图形
img = np.zeros((480,640,3),np.uint8)

# ●polylines(img, pts, isClosed, color[, thickness[, lineType[, shif]]) 画多边形
#pts 多边形的点集,必须是int 32 位的
pts = np.array([(250,100),(150,100),(50,280)],np.int32) #绘制三个点
cv2.polylines(img,[pts],True,(0,0,255),16)

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

在这里插入图片描述

如果将 True 改为 False 就表示不闭合 的形式

#绘制椭圆

import cv2
import numpy as np


#创建一个纯黑的背景图用来画图形
img = np.zeros((480,640,3),np.uint8)

# ●polylines(img, pts, isClosed, color[, thickness[, lineType[, shif]]) 画多边形
#pts 多边形的点集,必须是int 32 位的
pts = np.array([(250,100),(150,100),(50,280)],np.int32) #绘制三个点
# cv2.polylines(img,[pts],True,(0,0,255),16)
cv2.polylines(img,[pts],False,(0,0,255),16)#将True 改为 False 就表示不闭合

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

在这里插入图片描述

填充多边形:

●fillPoly 填充多边形 填写三和参数 即可 分别是 展示的画板、 绘制多边形的点 、 多边形的颜色

#绘制椭圆

import cv2
import numpy as np


#创建一个纯黑的背景图用来画图形
img = np.zeros((480,640,3),np.uint8)

# ●polylines(img, pts, isClosed, color[, thickness[, lineType[, shif]]) 画多边形
#pts 多边形的点集,必须是int 32 位的
pts = np.array([(250,100),(150,100),(50,280)],np.int32) #绘制三个点
# cv2.polylines(img,[pts],True,(0,0,255),16)
# cv2.polylines(img,[pts],False,(0,0,255),16)#将True 改为 False 就表示不闭合

# 填充形式的 多边型
cv2.fillPoly(img,[pts],(0,0,255))

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

在这里插入图片描述

绘制文本及中文文本:

文本
。text 要绘制的文本
。org文本在图片中的左下角坐标
。fontFace字体类型即字体
。fontScale字体大小

有的字体类型:在这里插入图片描述

绘制英文文本 :


import cv2
import numpy as np


#创建一个纯黑的背景图用来画图形
img = np.zeros((480,640,3),np.uint8)


# 绘制字体:
cv2.putText(img,'Hello OpenCV', (50,400),cv2.FONT_HERSHEY_COMPLEX,2,[0,0,255])


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

在这里插入图片描述

绘制中文文本:

导入本机中的字体 :

直接来到我们的windows文件中进行找到我们的中文字体文件 直接复制到我们写代码的路径下即可

在这里插入图片描述

输出:


# 使用OpenCV绘制中文
# 曲线救国   OpenCv是没有办法 直接绘制中文的
#使用Pillow 包  需要安装 pip install pillow
from PIL import ImageFont, ImageDraw , Image

img = np.full((500,500,3),fill_value = 255, dtype = np .uint8)

#导入字体文件
font = ImageFont.truetype('./MSYHBD.TTC')   #这个字体就是我们从windows中复制过来的字体

#创建一个pillow 的图片
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)


# 利用 draw 去绘制 中文
draw.text((10,50),'你好, 我好, 大家好', font = font ,fill = (0,255,0))


#重新变回 ndarray
img = np.array(img_pil)

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

在这里插入图片描述

是哟图片作为背景:


# 使用OpenCV绘制中文
# 曲线救国   OpenCv是没有办法 直接绘制中文的
#使用Pillow 包  需要安装 pip install pillow
from PIL import ImageFont, ImageDraw , Image

# 创建一个背景  如果要使用什好看的背景可以直接导入图片的
# img = np.full((500,500,3),fill_value = 255, dtype = np .uint8)
img =cv2.imread('./img1.png')  #读取图片

#导入字体文件
font = ImageFont.truetype('./MSYHBD.TTC')

#创建一个pillow 的图片
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)


# 利用 draw 去绘制 中文
draw.text((10,50),'你好, 我好, 大家好', font = font ,fill = (0,255,0))


#重新变回 ndarray
img = np.array(img_pil)

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

在这里插入图片描述

图像运算:

3.图像的算术与位运算
3.1图像的算术运算
3.1.1图像的加法运算
●add opencv使用add来执行图像的加法运算
图片就是矩阵,图片的加法运算就是矩阵的加法运算这就要求加法运算的两张图shape必须是相同的.

图像的相加:
图像和图像相加:

add 的规则就是两个图 对应位置的元素相加,如果超过255 ,全部变为255


#加法运算
import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#输出 图像信息 获取大小
print(man.shape)
print(woman.shape)

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_man = man[0:300,:270]
print(new_man.shape)


# 进行图像的加法运算需要图像的大小相同  通道相同 (维度相同)
# add 的规则就是两个图 对应位置的元素相加,如果超过255 ,全部变为255 
new_manwo = cv2.add(new_man,woman)

# 将三张图片堆叠展示 一波
# cv2.imshow('img',new_manwo)
cv2.imshow('img',np.hstack((man,woman,new_manwo)))

cv2.waitKey(0)
cv2.destroyAllWindows()


表示我们输出图像的像素点矩阵在这里插入图片描述

相加等于255 就会变为白色 因为 255 就是白色

在这里插入图片描述

图像和数字相加:

图片不仅可以和图片相加 还可以和数字进行相加 但是这里不同的是 我们超出255 的数字会被截断 相当于 取模运算(%256) 取模于256


#加法运算
import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')

#输出 图像信息 获取大小
print(man.shape)

#图片不仅可以和图片相加 还可以和数字进行相加 但是这里不同的是 我们超出255 的数字会被截断  相当于 取模运算(%256)  取模于256
print(man[:3,:3])  #输出其中的一部分 
man += 100

print(man[:3,:3])  #输出其中的一部分 

cv2.waitKey(0)
cv2.destroyAllWindows()


比如这里就是图像加上 100

在这里插入图片描述

图像相减:
图像减图像

减法 subtract() 方法 对应位置的元素相减 剪完之后小于0 统一变为 0

#减法运算
import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#输出 图像信息 获取大小
print(man.shape)
print(woman.shape)

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_man = man[0:300,:270]
print(new_man.shape)

#减法subtract 对应位置的元素相减 剪完之后小于0  同意变为 0 
new_manwo = cv2.subtract(man,woman)


# 将三张图片堆叠展示 一波
# cv2.imshow('img',new_manwo)
cv2.imshow('img',np.hstack((man,woman,new_manwo)))


cv2.waitKey(0)
cv2.destroyAllWindows()


经过相减之后我们就会发现我们的图像 越来越黑了 因为大多数的元素都变为了0 0 表示黑色

在这里插入图片描述

图像相乘:

#乘法 multiply 对应位置的元素相乘 乘完之后 大于255 同意变为255

#减法运算
import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#输出 图像信息 获取大小
print(man.shape)
print(woman.shape)

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_man = man[0:300,:270]
print(new_man.shape)


#乘法 multiply    对应位置的元素相乘  乘完之后 大于255 同意变为255
new_manwo = cv2.multiply(man,woman)



# 将三张图片堆叠展示 一波
# cv2.imshow('img',new_manwo)
cv2.imshow('img',np.hstack((man,woman,new_manwo)))


cv2.waitKey(0)
cv2.destroyAllWindows()


可以发现乘完之后我们的元素大多数都变为了255 也就是我们相乘之后的图片几乎变为了白色的样子

在这里插入图片描述

图像相除:
图像除图像

#除法 divide()

#减法运算
import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#输出 图像信息 获取大小
print(man.shape)
print(woman.shape)

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_man = man[0:300,:270]
print(new_man.shape)


#除法  divide()
new_manwo = cv2.divide(man,woman)



# 将三张图片堆叠展示 一波
# cv2.imshow('img',new_manwo)
cv2.imshow('img',np.hstack((man,woman,new_manwo)))


cv2.waitKey(0)
cv2.destroyAllWindows()


从中发现相除之后几乎都变为了0 能看的见的东西所剩无几
在这里插入图片描述

图像的融合

●cv2.addWsighted(src1, alpha, src2, beta, gamma)
●图片的融合操作相当于对图片进行线性运算w1x1 + w2 x2 + b.其中alpha是第一个权重参数, beta是
第二个权重参数, gamma是偏差.

不是简单的加法了 , 相当于拿图片做了一个线性运算

eg: new_img = img1 * w1 + img2 * w2 +bias

对上述的解释:

当我们想让合并出来的照片中 那个img1显示的更清楚一些还是img2显示的更清楚一些 就可以 使之相乘的 w 高一些

bias 表示 一些偏差 等(噪音)

# 不是简单的加法了 , 相当于拿图片做了一个线性运算
import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_manwo = man[0:300,:270]

new_img = cv2.addWeighted(new_manwo,0.3,woman,0.7,0) # 表示 男人的比重占0.3  女人站0.7   偏差为0 

cv2.imshow('img',np.hstack((man,woman,new_img)))


cv2.waitKey(0)
cv2.destroyAllWindows()


可以看到 明显 男人的图像要淡很多

在这里插入图片描述

我们想让图片整体变得暗一些 就直接将偏执 改为-100 等 更小的值

new_img = cv2.addWeighted(new_manwo,0.3,woman,0.7,-100)  #我们将融合之后的图像的明暗程度调的很低了

效果:

在这里插入图片描述

我们可以在很多场景下使用 这个偏差来进行温和我们的图像 使之可以随意变换的更暗或者明亮(调高)

注意点:

还有一点就是不建议将我们的权重使用 0 到100 之间的整数 或者更高 这样的话我们的图像 和权重相乘之后就全都大于或者等于255了 就会变的很白尽量使用0到1之间比较好

opencv中的位运算:

#openCV的逻辑运算 – 与或非 异或

openCv中的逻辑运算就是对应位置元素进行 与或非, 异或

204 & 213   #这个表示他们两个数字转换为 2进制 进行与运算之后 再转换为 10禁止进行输出

输出结果为 196

#opencv中的非运算 , 0 反过来是255

对图像进行非操作:

使用函数:

cv2.bitwise_not()
# 不是简单的加法了 , 相当于拿图片做了一个线性运算
import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#非操作
man_not = cv2.bitwise_not(man)
cv2.imshow('img',np.hstack((man,man_not)))
print(man[:2,:2])
print(man_not[:2,:2])


cv2.waitKey(0)
cv2.destroyAllWindows()

进行 了 一个取反的操作

在这里插入图片描述

之后我们对取反前和取反后的图片的 元素值进行输出对比 :细心就会发现我们的数值好像 就是使用255 对取反前的元素进行了一个相减 :

在这里插入图片描述

对图像进行与操作:

使用函数:

 cv2.bitwise_and()

import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_manwo = man[0:300,:270]

#与操作  两个图片对应位置的元素进行与操作 。所以我们还是要保证我们的两个图片之间的大小相同
man_and = cv2.bitwise_and(new_manwo,woman)
cv2.imshow('img',np.hstack((new_manwo,woman,man_and)))

print(new_manwo[:2,:2])
print("-------------------")
print(woman[:2,:2])
print("-------------------")
print(man_and[:2,:2])


cv2.waitKey(0)
cv2.destroyAllWindows()

我们将男人的图片和女人的图片进行了一个与操作 发现 黑的地方更黑 对应的进行了一个相与的操作

在这里插入图片描述

对数值进行分析: 也就是253和254 相与等于252在这里插入图片描述

对图像进行或运算:

使用函数:

cv2.bitwise_or()

import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_manwo = man[0:300,:270]

#与操作  两个图片对应位置的元素进行或操作 。所以我们还是要保证我们的两个图片之间的大小相同
man_or = cv2.bitwise_or(new_manwo,woman)
cv2.imshow('img',np.hstack((new_manwo,woman,man_or)))

print(new_manwo[:2,:2])
print("-------------------")
print(woman[:2,:2])
print("-------------------")
print(man_and[:2,:2])


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

数值分析:

253 或上 254 等255

在这里插入图片描述

对图像进行异或运算:

import cv2
import numpy as np

#读取图片
man = cv2.imread('./img3.jpg')
woman = cv2.imread('./img2.jpg')

#通过裁剪 将 图片变为一样大小  通过上面的输出 我们发现 女人的照片要比男人的照片小  于是我们将男人的照片切的和女人的一样即可
new_manwo = man[0:300,:270]

#与操作  两个图片对应位置的元素进行异或操作 。所以我们还是要保证我们的两个图片之间的大小相同
man_xor = cv2.bitwise_xor(new_manwo,woman)
cv2.imshow('img',np.hstack((new_manwo,woman,man_xor)))

print(new_manwo[:2,:2])
print("-------------------")
print(woman[:2,:2])
print("-------------------")
print(man_xor[:2,:2])


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

数值分析:在这里插入图片描述

图像的基本变换:

图像的放大与缩小

●resize(src, dsize[ dst[, fx[, fyL interpolation]l)
。src: 要缩放的图片
。dsize: 缩放之后的图片大小,元组和列表表示均可.
。dst:可选参数,缩放之后的输出图片
。fx, fy: x轴和y轴的缩放比,即宽度和高度的缩放比.
。interpolation 商on:插值算法,主要有以下几种:
■INTER_ NEAREST,邻近插值,度快,效果差.
■INTER_ LINEAR,双线性插值,使用原图中的4个点进行插值.默认.
■INTER_ CUBIC,三次插值,原图中的16个点
■INTER AREA,区域插值,效果最好,计算时间最长.

import cv2
import numpy as np

man = cv2.imread('./img1.png')
woman = cv2.imread('./img2.jpg')

print(man.shape)
print(woman.shape)

#输出大小  (773, 661, 3)   输出的是先行后列  所以下面要反过来去
#          (300, 270, 3)

#把男人缩放的和 女人一样大小 
new_manwo = cv2.resize(man,(270,300))    #注意点  : 我们resize 中大小的取值 是和原来的值相反的效果  因为我们输出别的行就是高度 列就是宽度  所以我们要先列后行

print(new_manwo.shape)
cv2.imshow('img',np.hstack((woman,new_manwo)))

cv2.waitKey(0)
cv2.destroyAllWindows()

注意点 :我们在对图像进行缩小或者方法的时候设置新图像的大小时要反过来设置 要先列后行 因为我们的 resize() 函数 的做法

在这里插入图片描述

这里我们使用 np.hstack() 函数将两个图拼在了一起 所以表明成功 因为我们的hstack()函数要想将两个图片堆叠展示 必须要两个图片的大小相同

在这里插入图片描述

四种效果的展示:

■INTER_ NEAREST,邻近插值,度快,效果差.
■INTER_ LINEAR,双线性插值,使用原图中的4个点进行插值.默认.
■INTER_ CUBIC,三次插值,原图中的16个点
■INTER AREA,区域插值,效果最好,计算时间最长.

import cv2
import numpy as np

man = cv2.imread('./img1.png')
woman = cv2.imread('./img2.jpg')

print(man.shape)
print(woman.shape)

#输出大小  (773, 661, 3)   输出的是先行后列  所以下面要反过来去
#          (300, 270, 3)

#把男人缩放的和 女人一样大小 
new_manwo = cv2.resize(man,(270,300))    #注意点  : 我们resize 中大小的取值 是和原来的值相反的效果  因为我们输出别的行就是高度 列就是宽度  所以我们要先列后行

#对图片进行缩放
new_woman1 = cv2.resize(woman,(640,480),interpolation = cv2.INTER_NEAREST)
new_woman2 = cv2.resize(woman,(640,480),interpolation = cv2.INTER_LINEAR)
new_woman3 = cv2.resize(woman,(640,480),interpolation = cv2.INTER_CUBIC)
new_woman4 = cv2.resize(woman,(640,480),interpolation = cv2.INTER_AREA)
#

print(new_manwo.shape)
cv2.imshow('img',np.hstack((woman,new_manwo)))
cv2.imshow('img1',new_woman1)
cv2.imshow('img2',new_woman2)
cv2.imshow('img3',new_woman3)
cv2.imshow('img4',new_woman4)


cv2.waitKey(0)
cv2.destroyAllWindows()
缩放:
import cv2
import numpy as np

man = cv2.imread('./img1.png')
woman = cv2.imread('./img2.jpg')



#还可以对图片按照x,y轴进行放大缩小  dsize=None 表示完全可以压缩  进行缩放0.5  长和宽豆各自一半
new_manwo = cv2.resize(man,dsize=None,fx=0.5,fy=0.5)    

print(new_manwo)

cv2.imshow('img',new_manwo)
cv2.imshow('img1',man)

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

数值分析:

在这里插入图片描述

图像的翻转与旋转:

图像的翻转:

●flip(sr, flipCode)
。flipCode =0表示上下翻转
。flipCode >0表示左右翻转
。flipCode <0上下+左右

#flip
import cv2
import numpy as np

man = cv2.imread('./img3.jpg')

#flipCode=0 表示上下翻转
#flipCode>0,表示左右翻转
#flipCode<0   , 表示上下左右翻转
new_man = cv2.flip(man,0);

cv2.imshow('img',np.hstack((man,new_man)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

图像的旋转

●rotate(img, rotateCode)
。ROTATE 90_CLOCKWISE 90度顺时针
。ROTATE ,180 180度,
。ROTATE 90 COUNTERCLOCKWISE 90度逆时针

注意点: 我们只能使用opencv中提供的这三种,不能自定义 暂时还没有哦!



#flip
import cv2
import numpy as np

man = cv2.imread('./img3.jpg')

# 。ROTATE 90_CLOCKWISE 90度顺时针
# 。ROTATE ,180 180度,
# 。ROTATE 90 COUNTERCLOCKWISE 90度逆时针
new_man = cv2.rotate(man,rotateCode = cv2.ROTATE_90_CLOCKWISE);

cv2.imshow('img',man)
cv2.imshow('img1',new_man)

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

仿射变换:

仿射变换之图像平移

●仿射变换是图像旋转,缩放,平移的总称。具体的做法是通过一个矩阵和和原图片坐标进行计算,得到新的坐
标, 完成变换.所以关键就是这个矩阵.
●warpAffine(src, M, dsize, flags, mode, value)
●M:变换矩阵
●dsize: 输出图片大小
●flag: 与resize中的插值算法一致
●mode:边界外推法标志
●value: 填充边界值
●平移矩阵

。矩阵中的每个像素由(x,y)组成,(x, y)表示这个像素的坐标假设沿x轴平移tx,沿y轴平移ty,那么最后得
到的坐标为(x,y)= (x + tx,y + ty),用矩阵表示就是:

在这里插入图片描述

以上表示的就是矩阵相乘:

在这里插入图片描述

以上的公式就表示 函数中的 M 如果我们要在x 轴上平移 就更改 tx 如果要在y轴上平移 就更改ty 的值。

import cv2
import numpy as np

man = cv2.imread('./img3.jpg')

h,w,ch = man.shape  #将图片的宽高 赋值

#变换矩阵  必须是32位的  因为要进行运算那些 
M = np.float32(([1,0,-200],[0,1,0]))# 表示值进行 x 上平移 y轴上不变(0)


#平移操作
#注意 opencv中是先宽度  后长度
new_man = cv2.warpAffine(man,M,dsize = (w,h))

cv2.imshow('img1',man)
cv2.imshow('img2',new_man)

cv2.waitKey(0)
cv2.destroyAllWindows()

表示向左平移 200:

在这里插入图片描述

仿射变换之获取变换矩阵
旋转:

仿射变换的难点就是计算变换矩阵, OpenCV提供了计算变换矩阵的API
●getRotationMatrix2D(center, angle, scale)
。center中心点,以图片的哪个点作为旋转时的中心点
。angle角度:旋转的角度,按照逆时针旋转.
。scale 缩放比例:想把图片进行什么样的缩放.

在(100,100)这个点 的位置 进行旋转15度

#在进行旋转操作的时候,不方便手动计算变换矩阵
#在opencv中 提供了获取变换矩阵的API
import cv2
import numpy as np

man = cv2.imread('./img3.jpg')

h,w,ch = man.shape  #将图片的宽高 赋值

#获取变换矩阵
M = cv2.getRotationMatrix2D((100,100),15,1)

new_man = cv2.warpAffine(man,M,(w,h))

cv2.imshow('img1',man)
cv2.imshow('img2',new_man)

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

按照逆时针的方式在中心点的位置旋转45度:

#获取变换矩阵
#规定按照逆时针进行旋转  如果要按照顺时针进行旋转就可以填写负数度数
M = cv2.getRotationMatrix2D((w/2,h/2),45,1)

在这里插入图片描述

以上就是 只有旋转的时候需要获取矩阵 平移的时候有固定的方法 矩阵。

既有旋转有移动:

●getAffineTransform(src[, dst[])通过三点可以确定变换后的位置,相当于解方程, 3个点对应三个方程,能
解出偏移的参数和旋转的角度.
。src原目标的三个点
。dst对应变换后的三E个点
在这里插入图片描述

我认为就是我们按住三个点之后 转动到我们指定的点的位置:


#三个点确定变换矩阵
#在opencv中 提供了获取变换矩阵的API
import cv2
import numpy as np

man = cv2.imread('./img3.jpg')

h,w,ch = man.shape  #将图片的宽高 赋值

src = np.float32([[200,100],[300,100],[200,300]])  #原始的点
dst = np.float32([[100,150],[360,200],[280,120]])  # 变换之后对应的点


# 需要原始图片的三个坐标, 和变换之后的三个对应的坐标
M = cv2.getAffineTransform(src,dst)

new_man = cv2.warpAffine(man,M,(w,h))

cv2.imshow('img1',man)
cv2.imshow('img2',new_man)

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

透视变换:

透视变换就是将一种坐标系变换成另一 种坐标系. 简单来说可以把一-张" 斜"的图变"正".
在这里插入图片描述

就比如说 上面作业 拍照的时候拍歪了, 我们可以使用透视变换将其拉正

我们可以通过的图片的四个点 使未拉正和拉正之后的位置 建立 联系:

在这里插入图片描述

●warpPerspective(img, M, dsize…)
●对于透视变换来说, M是一个3 * 3的矩阵.
●getPerspective Transform(src, dst) 获取透视变换的变换矩阵,需要4个点即图片的4个角.

import cv2
import numpy as np

img = cv2.imread('./img3.jpg')


shape = img.shape
print(shape)#输出的  高 ,宽 

#获取变换矩阵
#src 是原图的4个坐标  取你想要的四个点 圈成的一个矩形  
#顺序是 左上 右上 左下 右下 四个点的坐标
src = np.float32([[120,20],[270,20],[120,300],[270,300]])
#变换之后图片的四个点的新位置  
dst = np.float32([[0,0],[150,0],[0,280],[150,280]])

M = cv2.getPerspectiveTransform(src,dst)

#透视变换
new_img = cv2.warpPerspective(img,M,(150,280))   #(150,280) 表示最后图片的大小


cv2.imshow('img',img)
cv2.imshow('img1',new_img)

cv2.waitKey(0)
cv2.destroyAllWindows()

原始图片:在这里插入图片描述

这是变换后的照片

在这里插入图片描述

#变换之后图片的四个点的新位置  
dst = np.float32([[0,0],[150,0],[0,280],[150,280]])

在这里插入图片描述

滤波器

卷积:

什么是图片卷积
图像卷积就是卷积核在图像上按行滑动遍历像素时不断的相乘求和的过程

每个矩阵进行 运算相加就得出 Convolved Feature (卷积特征)中的一个值

在这里插入图片描述

步长
步长就是卷积核在图像.上移动的步幅.上面例子中卷积核每次移动一个像素步长的结果,如果将这个步长修改为2,
结果会如何?
为了充分扫描图片,步长-般设为1.

在这里插入图片描述

padding
从上面例子中我们发现,卷积之后图片的长宽会变小.如果要保持图片大小不变,我们需要在图片周围填充0.
padding指的就是填充的0的圈数.

在这里插入图片描述

我们可以通过公式计算出需要填充的0的圈数.
●输入体积大小H1 *W1 * D1

H1 表示高度, W1表示宽度 D1 表示放大数

●四个超参数:
。Filter数量K 表示为卷积盒数量
。Filter大小F 表示 卷积核 的大小 这里是3* 3 所以 F =3
。步长S
。零填充大小P 表示需要填充多少圈 0 也就是 padding
●输出体积大小H2* W2* D2
。H2=(H1-F+2P)/S+1
。W2=(W1-F+2P)/S+1
。D2=K

H2=(H1-F+2P)/S+1 的计算:

在这里插入图片描述

如果要保持卷积之后图片大小不变,可以得出等式:(N +2P- F+ 1)= N从而可以推导出P= F-1/2

H2=(H1-F+2P)/S+1 我们还是通过 这个公式来 理解 以上的公式

上面讲到 我们要使 卷积之后的图片个 原始图片的大小保持一直 那么我们就需要进行填充 0 ,所以我们需要计算出需要填充0 的圈数

比如说这里的高 H2=(H1-F+2P)/S+1 这是卷积之后的高 也就是卷积盒的高 我们要使卷积盒的高度和原来的照片的高度相同 就得出以下 公式 :

(H1-F+2P)/S+1 =H1

P = H1-H1/S+ F/S -1

但是呢 我们的步长 一般都为1 所以 S 可以省略不计 得:

P= F-1/2

所以我们这里的3* 3 的卷积核需要补 1 圈 零

P = F -1/2

=(3-1) /2

​ =1

卷积核的大小
图片卷积中,卷积核-般为奇数,比y如3* 3,5* 5,7* 7.为什么- -般是奇数呢,出于以下两个方面的考虑:
1.根据上面padding的计算公式,如果要保持图片大小不变,采用偶数卷积核的话,比如4 * 4,将会出现填充
圈零的情况.
2.奇数维度的过滤器有中心,便于指出过滤器的位置,即OpenCV卷积中的锚点.

对于第二点的解释: 就是我们的中心点在这里插入图片描述

卷积案例
●filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderTypell])
。ddepth是卷积之后图片的位深,即卷积之后图片的数据类型, - -般设为-1,表示和原图类型一致.
。kernel是卷积核大小,用元组或者ndarray表示,要求数据类型必须是float型.
。anchor 锚点即卷积核的中心点是可选参数,默认是(-1,-1)
。delta 可选参数,表示卷积之后额外加的一个值,相当于线性方程中的偏差,默认是0.
。borderType边界类型.-般不设.

import cv2
import numpy as np

# ●filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderTypell])
# 。ddepth是卷积之后图片的位深,即卷积之后图片的数据类型, - -般设为-1,表示和原图类型一致.

# 。anchor 锚点即卷积核的中心点是可选参数,默认是(-1,-1)
# 。delta 可选参数,表示卷积之后额外加的一个值,相当于线性方程中的偏差,默认是0.
# 。borderType边界类型.-般不设.
img = cv2.imread('./img3.jpg')

# 。kernel是卷积核大小,用元组或者ndarray表示,要求数据类型必须是float型.
 #表示每个像素点 都变为周围 5*5 的 平均值  对25进行相除求平
# 表示这个卷积核 是一个 5*5 的  其中的每个值 都是 1/25
kernel = np.ones((5,5),np.float32)/25 


#卷积操作
dst = cv2.filter2D(img,-1,kernel)

cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()


在这里插入图片描述

更改 卷积核:

# 尝试其他卷积操作:
kernel = np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]])   #表示 对周围进行一个弱化 突出中心点的值 突出轮廓

在这里插入图片描述

均值滤波和方盒滤波:

●boxFilter(src, ddepth, ksize[, dst[ anchorl, normalize[ borderTypel]l)方盒滤波.

ddepth是卷积之后图片的位深,即卷积之后图片的数据类型, - -般设为-1,表示和原图类型一致.。方盒滤波的卷积核的形式如下:

在这里插入图片描述

。normalize= True时,a= 1 / (W * H)滤波器的宽高
。normalize = False是. a = 1
。一般情况我们都使用normalize = True的情况.这时方盒滤波等价于均值滤波

●blur(src, ksize[, dst[ anchor[ borderTypell])均值滤波.

方盒滤波:
#方盒滤波
import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

#不用创建卷积核 只需要告诉方盒滤波,卷积的大小是多少
dst  = cv2.boxFilter(img,-1 ,(5,5),normalize = True)

cv2. imshow('img',np.hstack((img,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

均值滤波:

#均值滤波没有 位深这个参数 也就是 -1

#均值滤波
import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

#均值滤波没有 位深这个参数 也就是 -1  
dst  = cv2.blur(img,(5,5))

cv2. imshow('img',np.hstack((img,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

高斯滤波:

要理解高斯滤波首先要知道什么是高斯函数.高斯函数在是符合高斯分布(也叫正态分布)的数据的概率密度函数.
画出来长这样子:

在这里插入图片描述

高斯函数的特点是以x轴某一点(这一 点称为均值)为对称轴, 越靠近中心数据发生的概率越高,最终形成一个两边
平缓,中间陡峭的钟型(有的地方也叫帽子)图形.

高斯函数的一-般形式为:

在这里插入图片描述

高斯滤波就是使用符合高斯分布的卷积核对图片进行卷积操作.所以高斯滤波的重点就是如何计算符合高斯分布
的卷积核即高斯模板.
假定中心点的坐标是(0,0) ,那么取距离它最近的8个点坐标,为了计算,需要设定o的值。假定σ=1.5, 则模
糊半径为1的高斯模板就算如下:
在这里插入图片描述

总的来说 卷积核中 的值是符合高斯分布的

**注意点: e的0次方等于1,e的1次方等于e ** 零除以任何非零的数都得零

我们在这里 进行一个简单的测试案例:

eg: 计算 中间 (0,0) 的对应的高斯分布

直接套用公式:

G ( x , y ) = 1 2 π σ 2 e − x 2 + y 2 2 σ 2 G(x,y)= \frac {1} {2 \pi \sigma^2} e^ {- \frac {x^2+y^2} {2 \sigma^2}} G(x,y)=2πσ21e2σ2x2+y2

1/ (2 * pi * 1.5 **2)
# 计算 (0,0) 坐标点 ,对应的值   
#因为对应的 x和y 都是 0  所以 就等于 e的0次 等于1 故而我们可以直接计算2pi 那一坨   sigma就是我们上面指定的1.5 可以随机给 
1 / (2 * np.pi * 1.5 **2)

在这里插入图片描述

我们可以观察到越靠近中心,数值越大,越边缘的数值越小符合高斯分布的特点
通过高斯函数计算出来的是概率密度函数所以我们还要确保这九个点加起来为1,这9个点的权重总和等于
0.4787147,因此上面9个值还要分别除以0.4787147,得到最终的高斯模板。

注:有些整数高斯模板是在归-化后的高斯模板的基础上每个数除上左上角的值,然后取整.

在这里插入图片描述

有了卷积核,计算高斯滤波就简单了.假设现有9个像素点,灰度值(0-255) 的高斯滤波计算如下:

在这里插入图片描述

将这9个值加起来,就是中心点的高斯滤波的值。对所有点重复这个过程,就得到了高斯模糊后的图像。

总结:

总的来说就是我们通过上面的二维公式计算出 我们的高斯滤波器 (卷积核),之后对我们的图像进行卷积运算 就得出了我们的高斯滤波之后的图像:

对函数的应用:

●GaussianBlur(src, ksize, sigmaXI, dst[, sigmaY[ borderTypel]l)
。kernel 高斯核的大小.
。sigmaX, X轴的标准差
。sigmaY, Y轴的标准差,默认为0,这时sigmaY = sigmaX
。如果没有指定sigma值,会分别从ksize的宽度和高度中计算sigma.
●选择不同的sigma值会得到不同的平滑效果,sigma越大,平滑效果越明显.

import cv2
import numpy as np

img = cv2.imread("./img3.jpg")

#  sigmaX 越大  越模糊
# dst = cv2.GaussianBlur(img,(5,5),sigmaX = 10)

#当我们写 sigmaX=0(这就表示不写的形式) 的时候就会自动从KeySize中获取 
dst = cv2.GaussianBlur(img,(5,5),sigmaX =0)
    
cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

使用一张比较明显的图就可以发现 我们的高斯滤波 清楚了其中的细小的杂点:

在这里插入图片描述

中值滤波

中值滤波原理非常简单,假设有一个数组[1556789], 取其中的中间值(即中位数)作为卷积后的结果值即可

也就是我们卷积核的话就直接将卷积核中的所有数据都取出来 之后按照从小到大的排列 取中间的数

中值滤波对胡椒噪音(也叫椒盐噪音)效果明显,

import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

#中值滤波  这个滤波和我们 前两个滤波不一样 之前需要使用元组来书写 卷积核 现在只需要一个整数的信的形式
dst = cv2.medianBlur(img,5)

cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

效果图 展示: 使用这个中值滤波就会是我们的 类似有胡椒粉的 照片 滤出来比较干净

在这里插入图片描述

双边滤波:

双边滤波对于图像的边缘信息能过更好的保存。其原理为一个与空间距离相关的高斯函数与一个灰度距离相关的高斯函数相乘。

**空间距离:**指的是当前点与中心点的欧式距离。空间域高斯函数其数学形式为:

在这里插入图片描述

其中(xi,yi) 为当前点位置,(xc,yc) 为中心点的位置 , sigma为空间域标准差。

**灰度距离:**指的是当前点灰度与中心点灰度的差的绝对值。值域高斯函数其数学形式为:

在这里插入图片描述

其中gray(xi,yi)为当前点灰度值,gray(xc,yc)为中心点灰度值, sigma为值域标准差。
双边滤波本质.上是高斯滤波,双边滤波和高斯滤波不同的就是:双边滤波既利用了位置信息又利用了像素信息来定义滤波窗口的权重。而高斯滤波只用了位置信息.

对于高斯滤波,仅用空间距离的权值系数核与图像卷积后,确定中心点的灰度值。即认为离中心点越近的点,其权重系数越大。

双边滤波中加入了对灰度信息的权重,即在邻域内,灰度值越接近中心点灰度值的点的权重更大,灰度值相差大的点权重越小。此权重大小,则由值域高斯函数确定。
两者权重系数相乘,得到最终的卷积模板。由于双边滤波需要每个中心点邻域的灰度信息来确定其系数,所以其速度与比一般的滤波慢很多,而且计算量增长速度为核大小的平方。

在这里插入图片描述

双边滤波可以保留边缘,同时可以对边缘内的区域进行平滑处理.
双边滤波的作用就相当于做了美颜.

总结:

总的来说 ,这里我没有搞懂数学公式的原理,但是对高斯滤波的有点了解 其中的的原理 大概就是在高斯滤波的过滤上又做了一次灰度滤波 的效果,在灰度值比较大的地方只会对某一边或者是某部分就行高斯滤波模糊 其实直接跟着公式套即可。

●bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[ borderType]])
。sigmaColor是计算像素信息使用的sigma
。sigmaSpace是计算空间信息使用的sigma

d 就是 ksize

import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

dst = cv2.bilateralFilter(img,7,sigmaColor = 20,sigmaSpace = 50 )

cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

最后的测试 双边滤波对椒盐噪声 几乎是没有效果的 :

索贝尔(sobel)算子

边缘是像素值发生跃迁的位置,是图像的显著特征之一, 在图像特征提取,对象检测,模式识别等方面都有重要的作用。
人眼如何识别图像边缘?
比如有一幅图,图里面有一条线,左边很亮,右边很暗, 那人眼就很容易识别这条线作为边缘也就是像素的灰度值快速变化的地方.
sobel算子对图像求 一阶导数,一阶导数越大,说明像素在该方向的变化越大,边缘信号越强。
因为图像的灰度值都是离散的数字, sobel算子采用离散差分算子计算图像像素点亮度值的近似梯度.
图像是二维的,即沿着宽度/高度两个方向.
我们使用两个卷积核对原图像进行处理:

经过计算后 只剩下 垂直方向的边缘

在这里插入图片描述

我们使用 f(x) +f(x+2) 减去 f(x) - f(x-2) 就只剩中间的垂直

在这里插入图片描述

这样的话,我们就得到了两个新的矩阵,分别反映了每一-点像素在水平方向 上的亮度变化情况和在垂直方向.上的亮度变换情况.
综合考虑这两个方向的变化我们使用以下公式反映某个像素的梯度变化情况.

在这里插入图片描述

有时候为了简单起见,也直接用绝对值相加替代.G = |Gx| + |Gy|

import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

#注意sobel 要分别计算 x ,y 的梯度
#计算x轴方向的梯度  cv2.CV_64F 表示 位深   dx =1 ,dy = 0  表示 计算x轴的 不计算 y轴的
#只显示垂直方向的边缘
dx = cv2.Sobel(img,cv2.CV_64F,dx =1,dy = 0,ksize = 3)

# 计算y轴方向的 梯度 ,只有水平方向的边缘
dy = cv2.Sobel(img,cv2.CV_64F,dx=0,dy = 1,ksize = 3)

#最后使用sobel算子 别忘记了吧 x,y的梯度合并在一起
dst = cv2.add(dx,dy)

cv2.imshow('img1',img)
cv2.imshow('img2',np.hstack((dx,dy,dst)))




cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

教课 图片 我想 对这种有格子 需要展示出来的 很需要使用 把。在这里插入图片描述

沙尔(Scharr)算子

●Scharr(src, ddepth, dx, dy[ dst[, scale[, delta[ borderTye]l)
●当内核大小为3时,以上Sobel内核可能产生比较明显的误差(毕竟,Sobel算子只是求取了 导数的近似值)。为解决这- -问题,OpenCV提供了Scharr函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确.
●Scharr算子和Sobel很类似, 只不过使用不同的kernel值,放大了像素变换的情况:

在这里插入图片描述

●Scharr算子只支持3 * 3的kernel所以没有kernel参数了.
●Scharr算子只能求x方向或y方向的边缘.
●Sobel算子的ksize设为-1就是Scharr算子.
●Scharr擅长寻找细小的边缘- -般用的较少.

import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

#注意sobel 要分别计算 x ,y 的梯度
#计算x轴方向的梯度  cv2.CV_64F 表示 位深   dx =1 ,dy = 0  表示 计算x轴的 不计算 y轴的
#只显示垂直方向的边缘
dx = cv2.Scharr(img,cv2.CV_64F,dx =1,dy = 0)

# 计算y轴方向的 梯度 ,只有水平方向的边缘
dy = cv2.Scharr(img,cv2.CV_64F,dx=0,dy = 1)

#最后使用sobel算子 别忘记了吧 x,y的梯度合并在一起
dst = cv2.add(dx,dy)

cv2.imshow('img1',img)
cv2.imshow('img2',np.hstack((dx,dy,dst)))




cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

拉普拉斯算子(难):

索贝尔算子是模拟一阶求导,导数越大的地方说明变换越剧烈越有可能是边缘.

在这里插入图片描述

那如果继续对f’(t)求导呢?

在这里插入图片描述

可以发现"边缘处"的二阶导数=0,我们可以利用这一特性去寻找图像的边缘. 注意有一个问题,二阶求导为0的位置也可能是无意义的位置.

●拉普拉斯算子推导过程
。以x方向求解为例:
一阶差分: f’(x)= f(x)- f(x- 1)
二阶差分: f"(x)= f’(x+1)- f’(x)=(f(x+1)- f(x))-(f(x)- f(x- 1))
化简后: f"(x)= f(x- 1)- 2f(x))+ f(x+ 1)
同理可得: f"(y)= f(y- 1)-2f(y))+ f(y+ 1)
把x,y方向的梯度叠加在一起.

在这里插入图片描述

推算:

在这里插入图片描述

总的来说就是 先求一阶导再求二阶导 化简 再和矩阵一变换就可以得到公式了。

这样就得到了拉普拉斯算子的卷积核即卷积模板.

●Laplacian(src, ddepth[ dst[, ksize[, scale[, delta[, bordrTyel]]))
●可以同时求两个方向的边缘
●对噪音敏感,一般需要先进行去噪再调用拉普拉斯

import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

dst = cv2.Laplacian(img, -1 , ksize=3) 

cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

唯一的缺点就是对噪声的处理效果不好 ,所以我们在使用 拉普拉斯算子 进行边缘检测是后最好使用 高斯滤波器或者其他滤波器进行 去噪处理 最好。

比如这就是有噪声和椒盐的图片在这里插入图片描述

边缘检测Canny:

Canny边缘检测算法是John F. Canny于1986年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的最优算法最优边缘检测的三个主要评价标准是:

●低错误率:标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
●高定位性:标识出的边缘要与图像中的实际边缘尽可能接近。
●最小响应:图像中的边缘只能标识一次。

●Canny边缘检测的-般步骤
。去噪.边缘检测容易受到噪声影响,在进行边缘检测前通常需要先进行去噪,-般用高斯滤波去除噪声.
。计算梯度:对平滑后的图像采用sobel算子计算梯度和方向.
在这里插入图片描述

■梯度的方向被归为四类:垂直,水平和两个对角线.
■计算出来的梯度和方向大概如下图:

在这里插入图片描述

。非极大值抑制
■在获取了梯度和方向后,遍历图像,去除所有不是边界的点.
■实现方法:逐个遍历像素点判断当前像素点是否是周围像素点中具有相同方向梯度的最大值.
■"下图中,点A,B,C具有相同的方向,梯度方向垂直于边缘.
■”判断点A是否为A,B,C中的局部最大值,如果是,保留该点;否则,它被抑制(归零)

对上述的第二条进行解释 : 我们判断 A,B、C 谁的梯度值最大 。可以看出来的是A ,因为A在边缘上离边缘越近的点梯度值越大 所以我们就直接省略了B、和C

在这里插入图片描述

在这里插入图片描述

就是 同一方向的梯度值,取最大的即可 这样就是我们说的抑制了。之后其他值全部归零

。滞后阈值

在这里插入图片描述

当然阈值的大小 是我们自己来设定的。根据需求来吧

阈值越小 细节越丰富

在这里插入图片描述

函数:

●Canny(img, minVal, maxVal, …)

import cv2
import numpy as np

img = cv2.imread('./img3.jpg')

dst = cv2.Canny(img,100,200)

cv2.imshow('img1',img)
cv2.imshow('img2',dst)

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述


# 可以通过给小一点阈值 得到较为精细的边缘
dst2 = cv2.Canny(img,64,128)

在这里插入图片描述

形态学

形态学概述
●什么V古学
。指一系列处理图像形状特征的图像处理技准
。形态学的基本思想是利用一种特殊的结构元(本质上就是卷积核)来测量或提取输入图像中相应的形状或
特征,以便进一步进行图像分析和目标识别。
。这些处理方法基本是对二进制图像进行处理,即黑白图像
。卷积核决定着图像处理后的效果
。形态学常用基本操作有:
■膨胀和腐蚀
■开运算
■闭运算
■顶帽
■黑帽

图像全局二_值化

●工值化:将图像的每个像素变成两种值,比如0,255.
●threshold(src, thresh, maxval, type[, dst])
。src最好是灰度图
。thresh: 阈值
。maxval: 最大值,最大值不一定是255
。type: 操作类型.常见操作类型如下:

在这里插入图片描述

表示如果像素值大于阈值就设为最大值 否则就设为0

在这里插入图片描述

和上面相反 如果像素值大于阈值 就设为0 否者就是最大值

在这里插入图片描述

表示 如果你超过了阈值就会被截断 就等于阈值(等于抹平) 否者的话就还是你自己 不变

在这里插入图片描述

表示如果像素值大于阈值就等于自身 否者就会变为零

在这里插入图片描述

表示如果像素值大于阈值就是自己 否则久是本身

import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#二值化是对灰度图像进行操作的   所以我们先把图片变为 灰度图
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# ●threshold(src, thresh, maxval, type[, dst])
# 。src最好是灰度图
# 。thresh: 阈值
# 。maxval: 最大值,最大值不一定是255
# 。type: 操作类型.常见操作类型如下:
#注意 threashold 会返回两个值 一个是阈值   一个是二值化处理过的图片
thresh, dst = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY)

cv2.imshow('img' , np.hstack((img_gray,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

自适应阈值二值化

在前面的部分我们使用是全局阈值,整幅图像采用同一个数作为阈值。当时这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区 域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在高度不同的情况下得到更好的结果。

adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C dst=None)
这种方法需要我们指定六个参数,返回值只有一个。
●Adaptive Method-指定计算阈值的方法。

  • cv2.ADPTIVE THRESH MEAN_ C:阈值取自相邻区域的平均值
  • cv2.ADPTIVE THRESH GAUSSIAN C:阈值取值相邻区域的加权和,权重为一个高斯窗口。
  • Block Size -邻域大小(用来计算阈值的区域大小)。
  • C-这就是是一个常数,阈值就等于的平均值或者加权平均值减去这个常数。
import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#二值化是对灰度图像进行操作的   所以我们先把图片变为 灰度图
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)


#adaptiveMethod 表示指定阈值的方法
dst = cv2.adaptiveThreshold(img_gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,3,0)


cv2.imshow('img' , np.hstack((img_gray,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

总的来说,自适应二值化和全局二值化 的区别是 如果比较黑的话 全局二值化就无法将其细节显示出来 几乎都会识别为黑色。所以说对于需要的细节就会效果不好 所以我们可以使用自适应二值化进行就会好的多。

腐蚀操作

●腐蚀操作也是用卷积核扫描图像,只不过腐蚀操作的卷积和一般都是1,如果卷积核内所有像素点都是白色,那么锚点即为白色.

意思就是我们使用卷积核来进行扫描 如果其中有一个点是黑的就全部是黑的 只有全白才为白。

在这里插入图片描述

●大部分时候腐蚀操作使用的都是全为1的卷积核.

在这里插入图片描述

●erode(src, kernel[, dst[ anchor[, iterations[ borderType[ borderValelll)
。iterations是腐蚀操作的迭代次数,次数越多,腐蚀操作执行的次数越多,腐蚀效果越明显


import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#定义核
kernel = np.ones((7,7),np.uint8)

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

cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()

意思就是 当我么内进行操作的时候就会将那些嘘嘘给去掉 ,应为他们不能保证卷积核扫过去是全黑的效果。 可以通过定义卷积核进行操作 设置: 还可以通过设置 iterations 的 值来进行操作。
在这里插入图片描述

获取形态学卷积核

●weo提供了获取卷积核的api.不需要我们手工创建卷积核.
●getStructuringElement(shape, ksize[, anchor])
shape是指卷积核的形状,注意不是指长宽,是指卷积核中1形成的形状.
■MORPH_ RECT卷积核中的1是矩形,常用.
■MORPH_ ELLIPSE椭圆
■MORPH CROSS十字

import cv2
import numpy as np


kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
print(kernel)

#输出结果:
[[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]]

椭圆:

import cv2
import numpy as np


kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(15,15))
print(kernel)

输出结果

[[0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 1 1 1 1 1 1 1 1 1 0 0 0]
 [0 0 1 1 1 1 1 1 1 1 1 1 1 0 0]
 [0 1 1 1 1 1 1 1 1 1 1 1 1 1 0]
 [0 1 1 1 1 1 1 1 1 1 1 1 1 1 0]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [0 1 1 1 1 1 1 1 1 1 1 1 1 1 0]
 [0 1 1 1 1 1 1 1 1 1 1 1 1 1 0]
 [0 0 1 1 1 1 1 1 1 1 1 1 1 0 0]
 [0 0 0 1 1 1 1 1 1 1 1 1 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]]

十字:

import cv2
import numpy as np


kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(15,15))
print(kernel)
[[0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]]

使用以上三种卷积核进行测试: 进行腐蚀操作:


import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#定义核
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#椭圆
#kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(15,15))
#十字
#kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(15,15))

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

cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

膨胀操作

膨胀是腐蚀的相反操作,基本原理是只要保证卷积核的锚点是非0值,周边开论是0还是非0值,都变成非0值.

在这里插入图片描述

●dilate(img, kernel, iterations=1)

总的来说就是和腐蚀操作相反 瘦的可以变胖:


import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#定义核
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

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

cv2.imshow('img',np.hstack((img,dst)))

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

开运算:

开运算 = 腐蚀 + 膨胀

开运算提供了 另一种去除噪声的思路:

先使用我们 的 腐蚀 + 膨胀 进行测试一波

# 开运算 = 腐蚀 + 膨胀
# 
# 开运算提供了 另一种去除噪声的思路:
import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#先腐蚀
dst1 = cv2.erode(img,kernel,iterations=2)

#再膨胀
dst2 = cv2.dilate(dst1, kernel,iterations = 2)


cv2.imshow('img',np.hstack((img,dst1,dst2)))


cv2.waitKey(0)
cv2.destroyAllWindows()

效果:

在这里插入图片描述

总的来说就是 我们可以利用腐蚀操作将 噪声去除 之后再利用膨胀 恢复粗细大小 从而做到 去除噪声的操作。 如果噪声去除的不完全可以 调卷积核的大小 和 迭代的次数 来进行去除杂质

现在使用 开运算的函数 进行测试 可以做到一步到位的操作:

●开运算和闭运算都是腐蚀和膨胀的基本应用.
开运算=腐蚀+膨胀
●morphologyEx(img, MPRPH OPEN, kernel)
。MORPH_OPEN表示形态学的开运算 **表示为操作类型这个类型就表示开运算的 **
。kernel 如果噪点比较多,会选择大一点的kernel, 如果噪点比较小,可以选择小点的kernel

# 开运算 = 腐蚀 + 膨胀
# 
# 开运算提供了 另一种去除噪声的思路:
import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#获取卷积核 
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#进行开运算
dst = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel,iterations = 2)

cv2.imshow('img',np.hstack((img,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

会是一样的效果:

在这里插入图片描述

闭运算:

闭运算 = 膨胀+腐蚀

使用闭运算可以去除图形内部的噪声

●morphologyEx(img, MORPH_CLOSE, kernel)
。MORPH_CLOSE表示形态学的闭运算 **表示为操作类型这个类型就表示闭运算的 **
。kernel 如果噪点比较多,会选择大一点的kernel, 如果噪点比较小,可以选择小点的kernel

# 闭运算 = 膨胀+腐蚀
# 
# **使用闭运算可以去除图形内部的噪声
import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#获取卷积核 
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#进行闭运算
dst = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel,iterations = 2)

cv2.imshow('img',np.hstack((img,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

形态学梯度

●梯度=原图-腐蚀 (好想是 膨胀 - 腐蚀)
●腐蚀之后原图边缘变小了,原图-腐蚀就可以得到腐蚀掉的部分,即边缘.

●morphologyEx(img, MORPH_GRADIENT, kernel)
。MORPH_GRADIENT表示形态学的梯度运算 **表示为操作类型这个类型就表示梯度运算的 **
。kernel 如果噪点比较多,会选择大一点的kernel, 如果噪点比较小,可以选择小点的kernel

# 闭运算 = 膨胀+腐蚀
# 
# **使用闭运算可以去除图形内部的噪声
import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#获取卷积核 
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#进行闭运算
dst = cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel,iterations = 2)

cv2.imshow('img',np.hstack((img,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

顶帽运算:

●顶帽=原图-开运算
●开运算的效果是去除图形外的噪点,原图-开运算就得到了去掉的噪点.

●morphologyEx(img, MORPH_TOPHAT, kernel)
。MORPH_TOPHAT表示形态学的梯度运算 **表示为操作类型这个类型就表示梯度运算的 **
。kernel 如果噪点比较多,会选择大一点的kernel, 如果噪点比较小,可以选择小点的kernel

# 顶帽=原图-开运算  得到图形外的噪声


import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#获取卷积核 
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#
dst = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel,iterations = 1)

cv2.imshow('img',np.hstack((img,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

黑帽操作

●黑帽=原图-闭运算
●闭运算可以将图形内部的噪点去掉,那么原图-闭运算的结果就是图形内部的噪点

# 黑帽=原图-闭运算  得到图形内的噪声


import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#获取卷积核 
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#
dst = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel,iterations = 1)

cv2.imshow('img',np.hstack((img,dst)))


cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

图像轮廓

什么是图像轮廓;
图像轮廓是具有相同颜色或灰度的连续点的曲线.轮廓在形状分析和物体的检测和识别中很有用。
轮廓的作用:
●用于图形分析
●物体的识别和检测

注意点:
●为了检测的准确性,需要先对图像进行二值化或Canny操作。
●画轮廓时会修改输入的图像,如果之后想继续使用原始图像,应该将原始图像储存到其他变量中。

查找轮廓

●findContours(image, mode, method[, contours[, hierarchy[, offset])

。mode查找轮廓的模式

以下就是 mode 的种类模式

■RETR_ EXTERNAL = 0,表示只检测外围轮廓

在这里插入图片描述

■RETR_ LIST = 1,检测的轮廓不建立等级关系,即检测所有轮廓,较为常用

在这里插入图片描述

RETR_ CCOMP = 2,每层最多两级,从小到大,从里到外.
在这里插入图片描述

RETR TREE = 3,按照树型存储轮廓,从太到小从右到左.

在这里插入图片描述

。method 轮廓近似方法也叫ApproximationMode
■CHAIN_APPROX_ NONE保存所有轮廓上的点
■CHAIN_ APPROX_ SIMPLE,只保存角点比如四边形,只保留四边形的4个角,存储信息少,比较常

在这里插入图片描述

。返回contours和hierarchy即轮廓和层级

步骤

  1. 导入图片
  2. 转为黑白图片或者是灰度
  3. 之后进行二值化或者canny操作
  4. 查找轮廓
import cv2
import numpy as np

#  彩色 图片都是三通道的   (300, 270, 3)
img = cv2.imread('./img2.jpg')
print(img.shape)
# 我们要将其 变为 单通道的黑白图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(gray.shape)  #(300, 270)


# ●threshold(src, thresh, maxval, type[, dst])
# 。src最好是灰度图
# 。thresh: 阈值
# 。maxval: 最大值,最大值不一定是255
# 。type: 操作类型.常见操作类型如下:
#注意 threashold 会返回两个值 一个是阈值   一个是二值化处理过的图片
#之后进行 二值化   返回 两个结果 一个阈值 一个二值化之后的图 
thresh, binary = cv2.threshold(gray,150,255,cv2.THRESH_BINARY) 

#查找轮廓 新版本返回两个结果   contours和hierarchy
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

# contours是list 不是ndarray 里面放的是ndarray, 每个 ndarray表示一个contour
print(type(contours))
print(contours)
print(hierarchy)

# cv2.imshow('img1',img)
# cv2.imshow('img2',gray)
# cv2.imshow('img3',binary)

cv2.waitKey(0)
cv2.destroyAllWindows()
绘制轮廓:

●drawContours(image, contours, contourldx, color[, thickness[, lineType[ hierarchy[, maxLevel[,
offelel]])
。image要绘制的轮廓图像
。contours轮廓点
。contourldx 要绘制的轮廓的编号. -1 表示绘制所有轮廓
。color 轮廓的颜色,如(O, 0, 255)表示红色
。thickness线宽, -1表示全部填充

import cv2
import numpy as np

#  彩色 图片都是三通道的   (300, 270, 3)
img = cv2.imread('./img2.jpg')
print(img.shape)
# 我们要将其 变为 单通道的黑白图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(gray.shape)  #(300, 270)


# ●threshold(src, thresh, maxval, type[, dst])
# 。src最好是灰度图
# 。thresh: 阈值
# 。maxval: 最大值,最大值不一定是255
# 。type: 操作类型.常见操作类型如下:
#注意 threashold 会返回两个值 一个是阈值   一个是二值化处理过的图片
#之后进行 二值化   返回 两个结果 一个阈值 一个二值化之后的图 
thresh, binary = cv2.threshold(gray,150,255,cv2.THRESH_BINARY) 

#查找轮廓 新版本返回两个结果   contours和hierarchy
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

#绘制轮廓 会直接修改原图
#如果想保持原图不变,建议直接copy 一份 
img_copy = img.copy()
cv2.drawContours(img_copy,contours,-1,(0,0,255),2)

cv2.imshow('img1',img)
cv2.imshow('img2',img_copy)


cv2.waitKey(0)
cv2.destroyAllWindows()

通过 cv2.drawContours(img_copy,contours,-1,(0,0,255),2) 函数中的 contourldx 来查看轮廓的顺序等

在这里插入图片描述

轮廓的面积和周长

轮廓面积是指每个轮廓中所有的像素点围成区域的面积,单位为像素。
轮廓面积是轮廓重要的统计特性之一, 通过轮廓面积的大小可以进一步分析每 个轮廊隐含的信息,例如通过轮廓面积区分物体大小识别不同的物体。
在查找到轮廓后,可能会有很多细小的轮廓,我们可以通过轮廓的面积进行过滤.

●contourArea(contour)
●arcLength(curve, closed)
。curve即轮廓
。closed是否是闭合的轮廓

import cv2
import numpy as np

#  彩色 图片都是三通道的   (300, 270, 3)
img = cv2.imread('./shape1.png')
print(img.shape)
# 我们要将其 变为 单通道的黑白图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(gray.shape)  #(300, 270)


# ●threshold(src, thresh, maxval, type[, dst])
# 。src最好是灰度图
# 。thresh: 阈值
# 。maxval: 最大值,最大值不一定是255
# 。type: 操作类型.常见操作类型如下:
#注意 threashold 会返回两个值 一个是阈值   一个是二值化处理过的图片
#之后进行 二值化   返回 两个结果 一个阈值 一个二值化之后的图 
thresh, binary = cv2.threshold(gray,150,255,cv2.THRESH_BINARY) 

#查找轮廓 新版本返回两个结果   contours和hierarchy
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

#绘制轮廓 会直接修改原图
#如果想保持原图不变,建议直接copy 一份 
img_copy = img.copy()
cv2.drawContours(img_copy,contours,-1,(0,0,255),2)

#计算轮廓面积和周长

#面积
area = cv2.contourArea(contours[1])
print('area:',area)

# 周长 closed=True 表示我们的图形是不是封闭的  True 表示是闭区间  False表示不是闭区间
perimeter = cv2.arcLength(contours[1],closed=True)
print('perimeter:',perimeter)


cv2.imshow('img1',img)
cv2.imshow('img2',img_copy)


cv2.waitKey(0)
cv2.destroyAllWindows()

输出结果:

area: 12190.0
perimeter: 426.4264065027237
多边形逼近与凸包:

​ findContours后的轮廓信息contours可能过于复杂不平滑,可以用approxPolyDP函数对该多边形曲线做适当近似,这就是轮廓的多边形逼近.
approxPolyDP就是以多边形去逼近轮廓,采用的是Douglas- Peucker算法(方法名中的DP)
DP算法原理比较简单,核心就是不断找多边形最远的点加入形成新的多边形,直到最短距离小于指定的精度。

●approxPolyDP(curve, epsilon, closed[, approxCurve])
。curve 要近似逼近的轮廓
。epsilon 即DP算法使用的阈值
。closed轮廓是否闭合

import cv2
import numpy as np

#  彩色 图片都是三通道的   (300, 270, 3)
img = cv2.imread('./shape4.png')
print(img.shape)
# 我们要将其 变为 单通道的黑白图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(gray.shape)  #(300, 270)


# ●threshold(src, thresh, maxval, type[, dst])
# 。src最好是灰度图
# 。thresh: 阈值
# 。maxval: 最大值,最大值不一定是255
# 。type: 操作类型.常见操作类型如下:
#注意 threashold 会返回两个值 一个是阈值   一个是二值化处理过的图片
#之后进行 二值化   返回 两个结果 一个阈值 一个二值化之后的图 
thresh, binary = cv2.threshold(gray,150,255,cv2.THRESH_BINARY) 

#查找轮廓 新版本返回两个结果   contours和hierarchy
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

#绘制轮廓 会直接修改原图
#如果想保持原图不变,建议直接copy 一份 
# 像我们这种多边形的图  他有可能会返回多个轮廓的ndarray 所以我们要想显示那个操作就可以使用 轮廓的编号来查找一下  之后就可以进行面积逼近计算了
img_copy = img.copy()
cv2.drawContours(img_copy,contours,2,(0,0,255),2)


#使用多边形逼近 近手的轮廓   contours[2] 相当于是要绘制的轮廓的编号也一起输出了  这里表示我们要绘制编号位2的轮廓
#  阈值给的越大 逼近的效果就越粗糙  所以越小就越详细
approx = cv2.approxPolyDP(contours[2],5,closed=True)
#approx 本质上就是一个轮廓数据   是一个 ndarray 

#绘制出轮廓  approx 由于是一个 ndarray 所以就使用中括号括起来
cv2.drawContours(img_copy,[approx],-1,(0,255,0),2)


cv2.imshow('img1',img)
cv2.imshow('img2',img_copy)


cv2.waitKey(0)
cv2.destroyAllWindows()

**阈值给的越大 逼近的效果就越粗糙 所以越小就越详细 ** 绿色就是显示的就是 多边形逼近的轮廓显示

在这里插入图片描述

凸包:

逼近多边形是轮廊的高度近似,但是有时候,我们希望使用一个多边形的凸包来简化它。凸包跟逼近多边形很像,只不过它是物体最外层的凸多边形。凸包指的是完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。凸包的每一-处都是凸的, 即在凸包内连接任意两点的直线都在凸包的内部。在凸包内,任意连续三个点的内角小于180°。

我们希望使用尽可能少的线段将其包起来即可:在这里插入图片描述

总的来说 我的理解就是寻找 在轮廓上最远的几个分散点 进行连接 形成一个轮廓图。

●convexHull(points[ hull[, clockwise[, returnPointsll])
。points 即轮廓
。colckwise 顺时针绘制

import cv2
import numpy as py

hand = cv2.imread('./shape4.png')

#进行灰度化 或者是黑白 操作
gray = cv2.cvtColor(hand,cv2.COLOR_BGR2GRAY)


#进行二值化 
thresh, binary = cv2.threshold(gray,150,255,cv2.THRESH_BINARY) 

#查找轮廓
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

#绘制轮廓 会直接修改原图
#如果想保持原图不变,建议直接copy 一份 
img_copy = hand.copy()
cv2.drawContours(img_copy,contours,2,(0,0,255),2)

# 进行凸包计算   contours[2] 表示是根据 编号查找到的第三组轮廓 只有这个显示完全一点
hull = cv2.convexHull(contours[2])

#计算凸包的面积
area = cv2.contourArea(hull)
print('area:',area)

#画出凸包
cv2.drawContours(img_copy,[hull],-1,(0,255,0),2)

cv2.imshow('img',hand)
cv2.imshow('img2',img_copy)

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

外接矩形

外接矩形分为最小外接矩形和最大外接矩形.
下图中红色矩形是最小外接矩形,绿色矩形为最大外接矩形.

在这里插入图片描述

●minAreaRect(points) 最小外接矩阵
。points即为轮廓
。返回元组,内容是一个旋转矩形(RotatedRect)的参数: 矩形的起始坐标x,y,矩形的宽度和高度,矩形的
旋转角度.
●boundingRect(points) 最大外接矩阵
。points即为轮廓

最小外接矩形:

import cv2
import numpy as np

img = cv2.imread('./shape6.png')

#进行灰度化 或者是黑白 操作
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)


#进行二值化 
thresh, binary = cv2.threshold(gray,150,255,cv2.THRESH_BINARY) 

#查找轮廓
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)


#绘制轮廓
# cv2.drawContours(img,contours,2,(0,0,255),2)
#rect 是一个Rotated rect  旋转的矩形  矩形的起始坐标(x,y) 矩形的长宽 矩形的旋转角度
rect = cv2.minAreaRect(contours[0])

#这样就是帮我们吧旋转的四个坐标点计算出来了
#注意 坐标必须是整数的 所以需要转化一下  转换为 int 类型
box = cv2.boxPoints(rect)
print(box) 
#但是转换的时候我们还要进行四舍五入
box = np.round(box).astype('int64')
print(box)

#绘制最小外接矩形
cv2.drawContours(img,[box],0,(255,0,0),2)




cv2.imshow('img',img)

cv2.waitKey(0)
cv2.destroyAllWindows()




其中 是帮我们吧旋转的四个坐标点计算出来了

box = cv2.boxPoints(rect)

在这里插入图片描述

最大外接矩形:

import cv2
import numpy as np

img = cv2.imread('./shape6.png')

#进行灰度化 或者是黑白 操作
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)


#进行二值化 
thresh, binary = cv2.threshold(gray,150,255,cv2.THRESH_BINARY) 

#查找轮廓
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)


#绘制轮廓
# cv2.drawContours(img,contours,2,(0,0,255),2)
#rect 是一个Rotated rect  旋转的矩形  矩形的起始坐标(x,y) 矩形的长宽 矩形的旋转角度
rect = cv2.minAreaRect(contours[0])

#这样就是帮我们吧旋转的四个坐标点计算出来了
#注意 坐标必须是整数的 所以需要转化一下  转换为 int 类型
box = cv2.boxPoints(rect)
print(box) 
#但是转换的时候我们还要进行四舍五入
box = np.round(box).astype('int64')
print(box)

#绘制最小外接矩形
cv2.drawContours(img,[box],0,(255,0,0),2)



#最大外接矩形, 返回最大外接矩形的参数, (x,y) (w,h)
x,y,w,h = cv2.boundingRect(contours[0])

# 我们直接使用 我们之前学习的画矩形的方式来画即可
cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2)

cv2.imshow('img',img)

cv2.waitKey(0)
cv2.destroyAllWindows()




我们直接使用 我们之前学习的画矩形的方式来画即可 cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2)

注意 其中w h 需要加上x和y

在这里插入图片描述

图像金字塔

图像金字塔介绍
图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是-种以多分辨率来解释图像的有效但概念简单的结构。简单来说,图像金字塔是同一图像不同分辨率的子图集合.
图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一-张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将- -层一 -层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

在这里插入图片描述

常见两类图像金字塔

高斯金字塔( Gaussian pyramid): 用来向下/降采样,主要的图像金字塔
拉普拉斯金字塔(Laplacian pyramid):用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。

高斯金字塔:

高斯金字塔是通过高斯平滑和亚采样获得一系列下采样图像.
原理非常简单,如下图所示:

在这里插入图片描述

意思就是我们准备一个高斯卷积核 对图像进行卷积 操作 之后将卷积后的图像 去掉偶数行和列 之后就可以了。 这样就会形成一个固定缩小比例。

如果图片是基数也会进行四舍五入的进行 去掉

原始图像MN->处理后图像M/2N/2.
每次处理后,结果图像是原来的1/4.

在这里插入图片描述

注意:向下采样会丢失图像信息. 也就是说我们的图像会越来越不好

函数:

cv2.pyrDown()

import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

dst = cv2.pyrDown(img)

cv2.imshow('img',img)
cv2.imshow('img1',dst)

cv2.waitKey(0)
cv2.destroyAllWindows()

我们可以进行多次向下采样 也就是说我们可以多次调用 pyrDown() 进行向下采样

在这里插入图片描述

●向上取样
向上取样是向下取样的相反过程,是指图片从小变大的过程.

在这里插入图片描述

1.将图像在每个方向扩大为原来的两倍,新增的行和列以0填充
2.使用先前同样的内核(乘以4)与放大后的图像卷积,获得近似值

函数:

cv2.pyrUp()

import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

dst = cv2.pyrUp(img)
cv2.imshow('img',img)
cv2.imshow('img1',dst)

cv2.waitKey(0)
cv2.destroyAllWindows()

同样可以进行多次操作 调用函数

在这里插入图片描述

拉普拉斯金字塔:

在这里插入图片描述

将降采样之后的图像再进行上采样操作,然后与之前还没降采样的原图进行做差得到残差图!为还原图像做信息的准备!
也就是说,拉普拉斯金字塔是通过源图像减去先缩小后再放大的图像的一系列图像构成的。保留的是残差!
拉普拉斯金字塔是由高斯金字塔构成的,没有专门的函数.
拉普拉斯金字塔图像只像图像边缘,它的大部分元素都是0,用于图像压缩.

在这里插入图片描述

先放大 再缩小:

import cv2
import numpy as np

img = cv2.imread('./img2.jpg')

#先缩小
dst = cv2.pyrDown(img)
#再放大
dst = cv2.pyrUp(dst)
cv2.imshow('img',img)
cv2.imshow('img1',dst)

#展示数据中缺失的样例
cv2.imshow('miss',img - dst)

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

图像直方图

1.图想直方图的基本概念
在统计学中,直方图是- -种对数据分布情况的图形表示,是一种二维统计图表.
图像直方图是用一表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布的直方图。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此,- -张较暗图片的图像直方图中的数据多集中于左侧和中间部分,而整体明亮、只有少量阴影的图像则相反。
●横坐标:图像中各个像素点的灰度级.
●纵坐标:具有该灰度级的像素个数.

在这里插入图片描述

在这里插入图片描述

画出上图的直方图:
在这里插入图片描述

或者以柱状图的形式:

在这里插入图片描述

●归一化直方图
。横坐标:图像中各个像素点的灰度级
。纵坐标:出现这个灰度级的概率

在这里插入图片描述

在这里插入图片描述

●直方图术语:
dims:需要统计的特征的数目。例如: dims=1, 表示我们仅统计灰度值。
bins:每个特征空间子区段的数目。

在这里插入图片描述

在这里插入图片描述

range: 统计灰度值的范围,一般为[0, 255]

使用OpenCV统计直方图

●calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate])
。images: 原始图像
。channels:指定通道.
■需要用中括号括起来,输入图像是灰度图像是,值是[0],彩色图像可以是[0], [1], [2],分别对应
B,G,R.
。mask: 掩码图像
■统计整幅图像的直方图,设为None
■统计图像某一 部分的直方图时,需要掩码图像.
。histSize: BINS的数量
■需要用中括号括起来,例如[256] 因为从零开始 所以是256 个
。ranges: 像素值范围,例如[0, 255]
。accumulate: 累积标识

■默认值为False
■如果被设置为True,则直方图在开始分配时不会被清零.
■该参数允许从多个对象中计算单个直方图,或者用于实时更新直方图.
■多个直方图的累积结果,用于对一组图像计算直方图.

import cv2
import numpy as np

img = cv2.imread('./img2.jpg')
  
# [0] 统计通道 BGR  是蓝色的   mask 没有就填写None
hist = cv2.calcHist([img],[0],None,[256],[0,255])

print(hist)  #这个是二维的

在这里插入图片描述

绘制直方图:

使用 matplotlib 直接进行操作 绘制直方图:

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

img = cv2.imread('./img2.jpg')

#转换为 灰度图
gray =  cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
  
#统计直方图数据
plt.hist(gray.ravel(),bins = 256,range = [0,255])

在这里插入图片描述

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


img = cv2.imread('./img2.jpg')
  
# [0] 统计通道 BGR  是蓝色的   mask 没有就填写None
histb = cv2.calcHist([img],[0],None,[256],[0,255])
histg = cv2.calcHist([img],[1],None,[256],[0,255])
histr = cv2.calcHist([img],[2],None,[256],[0,255])

plt.plot(histb,color='b',label = 'blue')
plt.plot(histg,color='g',label = 'green')
plt.plot(histr,color='r',label = 'red')
plt.legend()  #显示图例
plt.show()

使用opencv进行统计 还是使用 matplotlib 来进行绘制

在这里插入图片描述

使用掩膜直方图:

●掩膜
在这里插入图片描述

●如何生成掩膜
。先生成- -个全黑的和原始图片大小一样大的图片. mask = np.zeros(image.shape, np.uint8)
。将想要的区域通过索引方式设置为255. mask[100:200,200: 300] = 255

总的来说 我们使用这个mask 进行了处理 我们截取了其中一部分 较好的区域 进行 直方图展示

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

img = cv2.imread('./img2.jpg')

#转换为 灰度图
gray =  cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#生成掩膜图像
mask = np.zeros(gray.shape,np.uint8)

hist_mask = cv2.calcHist([gray],[0],mask,[256],[0,255])
hist_gray = cv2.calcHist([gray],[0],None,[256],[0,255])
# 绘制直方图
plt.plot(hist_mask,label = 'mask')
plt.plot(hist_gray,label = 'gray')
plt.legend()

#设计想要统计的直方图区域
mask[90:250,100:250]=255
cv2.imshow('mask',mask)
#  gray 和gray 做与运算 还是gray ,mask 的作用就是gray 和gray先做 与运算,结果在和mask做与运算
cv2.imshow('mask_gray',cv2.bitwise_and(gray,gray,mask=mask))

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

特征检测:

特征点检测和匹配
1.特征检测的基本概念
特征检测是计算机视觉和图像处理中的一一个概念。它指的是使用计算机提取图像信息,决定每个图像的点是否属于
一个图像特征。特征检测的结果是把图像上的点分为不同的子集,这些子集往往属于孤立的点、连续的曲线或者连
续的区域。
特征检测包括边缘检测,角检测,区域检测和脊检测.
特征检测应用场景:
●图像搜索,比如以图搜图
●拼图游戏
●图像拼接

●寻找特征
●特征是唯一的
●特征是可追踪的
●特征是能比较的

在这里插入图片描述

我们发现:
。平坦部分很难找到它在原图中的位置
。边缘相比平坦要好找- -些, 但是也不能一下确定
。角点可以一下就找到其在原图中的位置

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

在图像特征中最重要的就是角点.哪些是角点呢?
●灰度梯度的最大值对应的像素
●两条线的交点
●.极值点(一阶导数最大,二阶导数为0)

泰勒展开公式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-olCoIGeB-1690355241649)(openCV.assets/image-20230323193556487.png)]

Harris角点检测

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jUjyLb9e-1690355241650)(openCV.assets/image-20230323175430909.png)]

Harris角点检测原理

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

在这里插入图片描述

在这里插入图片描述

反推导:在这里插入图片描述

在这里插入图片描述

其实就是一个椭圆方程 :

在这里插入图片描述

什么是实对称矩阵?

**如果有n阶矩阵A,其矩阵的元素都为实数,且矩阵A的转置等于其本身(aj=aj) , (ij为元素的脚标) ,则称A为实对称矩阵。 **

因为我们的四个位置算出来也肯定是一个整数 所以是一个实对称矩阵

.n阶实对称矩阵A必可相似对角化,且相似对角阵上的元素即为矩阵本身特征值。

在这里插入图片描述

在这里插入图片描述

所以最终 我们的椭圆的大小就和我们的拉姆达有关 拉姆达越大 椭圆越大

在这里插入图片描述

在这里插入图片描述

总的来说 就是 如果 是角点的话 我们的椭圆就很正常并且很大, 如果是平坦区就是一个小小的圆。其实最终还是要通过 拉姆达1和拉姆达二之间的差距进行判断

如果我们要想判断是否是角点 就比较麻烦 难道我们还需要在求出之后 进行比较两个 拉姆达之间的值吗?太过于 麻烦 所以我们就可以通过下面的方法进行操作:
在这里插入图片描述

阿尔法是我们自定义给定的值 一般是在 0.04~0.06之间 最后的结果 根据R来判断 R越大我们的椭圆就越大 也就意味着它是一个角点

检测窗口在图像上移动, .上图对应着三种情况:
●在平坦区域无论向哪个方向移动,衡量系变换不大
●边缘区域,垂直边缘移动时,衡量系统变换剧烈.
●在角点处,往哪个方向移动,衡量系统都变化剧烈.
●cornerHarris(src, blockSize, ksize, k[ dst[ borderType])
。blockSize: 检测窗口大小
。ksize: sobel的卷积核
。k:权重系数,即上面公式中的a ,是个经验值, 一般取0.04~0.06之间.一般默认0.04

import cv2
import numpy as np

img = cv2.imread('./shape7.png')

#转换为 灰度图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#角点检测
#没有要求必须是奇数的   取小一点比较好 容易检测出
#返回角点响应 每一个像素都能计算出一个角点响应
dst = cv2.cornerHarris(gray,blockSize = 2,ksize=3,k=0.04)
print(gray.shape)
print(dst)
print(dst.shape)
print(type(dst))

#显示角点
#设置阈值 dst.max()表示将dst中的最大值取出来  
#  dst > (0.01 * dst.max()) # 表示 dst中的最大值的0.01倍我们认为是角点 
    
#之后在图片img中进行一个筛选   [0,0,255]   表示找出角点之后我们将所有点都显示为红色  
#可以根据调整阈值的大小来进行显示角点很判断
img[dst > (0.01 * dst.max())] = [0,0,255]  

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

这样我们就找到其中所有的角点了:

在这里插入图片描述

Shi-Tomasi角点检测

●Shi-Tomasi是Harris角点检测的改进.
●Harris角点检测计算的稳定性和K有关,而K是一个经验值,不太好设定最佳的K值.
●Shi-Tomasi 发现,角点的稳定性其实和矩阵M的较小特征值有关,于是直接用较小的那个特征值作为分数。这样就不用调整k值了。
。Shi-Tomasi 将分数公式(以上harris中的角点响应)改为如下形式: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UFQ6HpIo-1690355241653)(openCV.assets/image-20230324083044108.png)]
。和Harris 一样,如果该分数大于设定的阈值,我们就认为它是一个角点。
●goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance[, corners[, mask[, blockSize[,useHarrisDetector[ k]]]]])
。maxCorners: 角点的最大数值为0表示无限制
。qualityLevel: 角点质量,小于1.0的整数,一般在0.01-0.1之间.
。minDistance:角之间最小欧式距离,忽略小于此距离的点
。mask: 感兴趣的区域
。blockSize: 检测窗口大小
。useHarrisDetector: 是否使用Harris算法.
。k:默认是0.04

注意 : useHarrisDetector 表示 是否使用Harris算法. 如果使用的话就要给k也带上

什么是欧式距离?

欧式距离也称欧几里得距离,是最常见的距离度量,衡量的是多维空间中两个点之间的 绝对距离

以古希腊数学家欧几里得命名的距离,也就是我们直观的两点之间直线最短的直线距离。

在这里插入图片描述

欧氏距离定义: 欧氏距离( Euclidean distance)是一个通常采用的距离定义,它是在m维空间中两个点之间的真实距离。

在二维和三维空间中的欧式距离的就是两点之间的距离,二维的公式是:

在这里插入图片描述

三维的公式是:

在这里插入图片描述

推广到n维空间,欧式距离的公式是:

在这里插入图片描述

n维欧氏空间是一个点集,它的每个点可以表示为(x(1), x(2), …, x(n)),其中x(i)(i=1,2…n)是实数称为x的第i个坐标,两个点x和y之间的距离d(x, y)定义为上面的公式。

我么在读取图片的时候就可以通过flags 指定 读取进来的是灰度图像还彩色图像 不写默认是彩色 写了就可以读取灰色

这样就可以不用进行灰度化了

img = cv2.imread('shape7.png',flags = cv2.IMREAD_GRAYSCALE)  
import cv2
import numpy as np

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

#灰度化
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#Shi-Tomasi角点检测
# ●goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance[, corners[, mask[, blockSize[,useHarrisDetector[ k]]]]])
# 。maxCorners: 角点的最大数值为0表示无限制
# 。qualityLevel: 角点质量,小于1.0的整数,一般在0.01-0.1之间.
# 。minDistance:角之间最小**欧式距离**,忽略小于此距离的点
# 。mask: 感兴趣的区域
# 。blockSize: 检测窗口大小
# 。useHarrisDetector: 是否使用Harris算法.
# 。k:默认是0.04
#返回的是都是 坐标
corners = cv2.goodFeaturesToTrack(gray,maxCorners=0,qualityLevel=0.01,minDistance= 10)
print(corners.shape)
# print(type(corners))

#变为整形
corners = np.int0(corners)


#画出角点
for i in corners:
    #i相当于corners中的每一行数据
    #ravel()把二维变一维了  即角点的坐标点
    x, y = i.ravel()
    # 参数解释 :  图片、 圆心坐标 、 半径、 颜色、 是否填充
    cv2.circle(img,(x,y),3,(0,0,255),-1)
    
cv2.imshow('Shi-Tomasi',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

A

可以看出角点数量一共有215个

在这里插入图片描述

可以通过在函数中设置 maxCorners 和 qualityLevel 还有minDistance 进行控制数量

猜你喜欢

转载自blog.csdn.net/qq_63946922/article/details/131939742