OpenCV2-图像基本操作-阈值与平滑处理-形态学-梯度运算

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • [1. 图像基本操作](#1. 图像基本操作)
    • [1.0 基本操作](#1.0 基本操作)
    • [1.1 视频的读取与处理](#1.1 视频的读取与处理)
    • [1.2 截取部分图像数据](#1.2 截取部分图像数据)
    • [1.3 颜色通道提取](#1.3 颜色通道提取)
    • [1.4 边界填充](#1.4 边界填充)
    • [1.5 数值计算](#1.5 数值计算)
    • [1.6 图像融合](#1.6 图像融合)
  • [2. 阈值与平滑处理](#2. 阈值与平滑处理)
    • [2.1 图像阈值](#2.1 图像阈值)
    • [2.2 图像平滑](#2.2 图像平滑)
    • [2.3 高斯与中值滤波](#2.3 高斯与中值滤波)
  • [3. 图像形态学操作](#3. 图像形态学操作)
    • [3.1 形态学-腐蚀操作](#3.1 形态学-腐蚀操作)
    • [3.2 形态学-膨胀操作](#3.2 形态学-膨胀操作)
    • [3.3 开运算与闭运算](#3.3 开运算与闭运算)
    • [3.4 梯度运算](#3.4 梯度运算)
    • [3.5 礼帽与黑帽](#3.5 礼帽与黑帽)
  • [4. 图像梯度运算](#4. 图像梯度运算)
    • [4.1 图像梯度-Sobel算子](#4.1 图像梯度-Sobel算子)
    • [4.2 图像梯度-Scharr算子和laplacian算子](#4.2 图像梯度-Scharr算子和laplacian算子)
  • 总结

前言

1. 图像基本操作

1.0 基本操作

java 复制代码
import cv2 #opencv读取的格式是BGR
import matplotlib.pyplot as plt
import numpy as np 
%matplotlib inline

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

%matplotlib inline 是一个特殊的 IPython 魔法命令

执行该命令后,后续所有 plt.plot()、plt.imshow() 等绘图命令的结果会直接显示在代码单元格的下方

图像会被保存为静态图片嵌入到笔记本中,方便保存和分享

不需要再额外调用 plt.show() 来显示图像(不过调用了也不会有问题)

java 复制代码
#图像的显示,也可以创建多个窗口
cv2.imshow('image',img) 
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(10000) 
cv2.destroyAllWindows()

过了十秒,弹窗就自动消失了

java 复制代码
def cv_show(name,img):
    cv2.imshow(name,img) 
    cv2.waitKey(0) 
    cv2.destroyAllWindows()

创建了一个自动显示的函数

java 复制代码
img=cv2.imread('../img/cat.jpg',cv2.IMREAD_GRAYSCALE)
img

这个是以灰度图的形式展示

java 复制代码
#图像的显示,也可以创建多个窗口
cv2.imshow('image',img) 
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(10000) 
cv2.destroyAllWindows()
java 复制代码
#保存
cv2.imwrite('mycat.png',img)


java 复制代码
type(img)
java 复制代码
img.size
java 复制代码
img.dtype

1.1 视频的读取与处理

视频也是由图片组成的

每一帧都是一个静止的图像,这样就可以了

每s30帧,就是一秒30张图---》我们正常人类就看不出来是卡的

每秒15帧,图片之间的间隔比较大--》比较卡,看着

把视频拆分为每一帧就可以操作了

java 复制代码
vc = cv2.VideoCapture('../img/test.mp4')
java 复制代码
# 检查是否打开正确
if vc.isOpened(): 
    open, frame = vc.read()
else:
    open = False

vc.read()就是读取第一帧

然后就是第二帧,一直读取

这一帧读取成功了,那么oepn就是true

frame 就是这一帧的图像数据

vc.read() 是读取一帧图像的方法

返回两个值:

open(布尔值):表示是否成功读取到帧

frame(numpy 数组):读取到的图像帧数据(BGR 格式)

这行代码的作用是尝试从设备中读取第一帧图像

java 复制代码
while open:
    ret, frame = vc.read()
    if frame is None:
        break
    if ret == True:
        gray = cv2.cvtColor(frame,  cv2.COLOR_BGR2GRAY)
        cv2.imshow('result', gray)
        if cv2.waitKey(10) & 0xFF == 27:
            break
vc.release()
cv2.destroyAllWindows()

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

将彩色帧(BGR 格式)转为灰度图

cv2.imshow('result', gray)

在名为 result 的窗口中显示灰度图

if cv2.waitKey(10) & 0xFF == 27:

等待 10 毫秒,检测键盘输入

27 是 ASCII 码,对应键盘上的 Esc 键

break

如果按下 Esc 键,退出循环

vc.release()

释放视频捕获设备资源(如摄像头)

cv2.destroyAllWindows()

关闭所有 OpenCV 创建的窗口

循环读取视频帧 → 转为灰度图 → 显示图像 → 检测到 Esc 键或无有效帧时退出 → 释放资源

cv2.waitKey(10)

这是 OpenCV 的键盘输入等待函数。

参数 10 表示等待 10 毫秒(单位:毫秒)。在这段时间内,如果用户按下任何键,函数会返回该键的 ASCII 码;如果没有按键,返回 -1。

作用:给系统留出时间处理窗口事件(比如显示图像),同时检测键盘输入。

& 0xFF

这是一个位运算,用于提取按键值的低 8 位(即 0-255 的范围)。

因为 cv2.waitKey() 的返回值在不同系统上可能是 32 位整数(高 24 位可能包含其他信息),通过与 0xFF(二进制 11111111)做与运算,可以确保只保留有效的 ASCII 码部分。

== 27

27 是 ASCII 码中对应的 Esc 键(Escape 键)。

整个条件的意思是:如果用户在 10 毫秒内按下了 Esc 键,则条件成立。

每次都会在这里等10ms

这样就变成了灰度视频了

java 复制代码
        if cv2.waitKey(100) & 0xFF == 27:

如果是这样的话,就很慢了

1.2 截取部分图像数据

java 复制代码
#截取部分图像数据
img=cv2.imread('../img/cat.jpg')
cat=img[0:200,0:200] 
cv_show('cat',cat)

1.3 颜色通道提取

java 复制代码
b,g,r=cv2.split(img)


java 复制代码
img=cv2.merge((b,g,r))
img.shape

这样就可以1组合起来了

java 复制代码
# 只保留R
cur_img = img.copy()
#bgr,所以b的索引为0
cur_img[:,:,0] = 0
cur_img[:,:,1] = 0
cv_show('R',cur_img)

这样就是只有R了

java 复制代码
# 只保留G
cur_img = img.copy()
cur_img[:,:,0] = 0
cur_img[:,:,2] = 0
cv_show('G',cur_img)

1.4 边界填充

java 复制代码
#上下左右分别填充的大小
top_size,bottom_size,left_size,right_size = (50,50,50,50)

replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_CONSTANT, value=0)

只是后面的填充方法不一样而已

java 复制代码
import matplotlib.pyplot as plt
plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL')
plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')

plt.show()

plt.subplot(231) 是 Matplotlib 中用于创建子图的函数,其参数 231 是一个三位数的缩写,具体含义如下:

第一位数字 2 表示行数:整个图像区域将被分成 2 行

第二位数字 3 表示列数:整个图像区域将被分成 3 列

第三位数字 1 表示位置索引:当前子图位于第 1 个位置

java 复制代码
BORDER_REPLICATE:复制法,也就是复制最边缘像素。
BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
BORDER_CONSTANT:常量法,常数值填充。

第一个是原图

1.5 数值计算

java 复制代码
img_cat=cv2.imread('../img/cat.jpg')
img_dog=cv2.imread('../img/dog.jpg')
java 复制代码
img_cat2= img_cat +10 
img_cat[:5,:,0]

加法就是在每一个位置上都加上10

java 复制代码
#相当于% 256
(img_cat + img_cat2)[:5,:,0] 

142+152=294

294%256=38

java 复制代码
cv2.add(img_cat,img_cat2)[:5,:,0]

142+152=294~~255

1.6 图像融合

java 复制代码
img_cat + img_dog

这样是不行的额,因为行列不一样

我们把它们变为一样的

java 复制代码
img_dog = cv2.resize(img_dog, (500, 414))
img_dog.shape
java 复制代码
res = cv2.resize(img, (0, 0), fx=3, fy=1)
plt.imshow(res)

(0, 0) 是 dsize 参数的值,表示不直接指定输出图像的尺寸,而是通过后面的缩放因子 fx 和 fy 来计算新尺寸

fx=3 表示水平方向放大 3 倍

fy=1 表示垂直方向保持原尺寸不变

java 复制代码
res = cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0)

这个表示按照权值相加

但是前提肯定是长宽相同

java 复制代码
plt.imshow(res)
plt.show()

2. 阈值与平滑处理

2.1 图像阈值

java 复制代码
ret, dst = cv2.threshold(src, thresh, maxval, type)
src: 输入图,只能输入单通道图像,通常来说为灰度图

dst: 输出图

thresh: 阈值

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

type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV

cv2.THRESH_BINARY           超过阈值部分取maxval(最大值),否则取0

cv2.THRESH_BINARY_INV    THRESH_BINARY的反转

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

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

cv2.THRESH_TOZERO_INV  THRESH_TOZERO的反转
java 复制代码
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)

titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in range(6):
    plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

THRESH_BINARY表示当前像素点大于127的话,那么就赋值为255,大于127,有点亮,255就超级亮--》白点

小于阈值就取0---》黑点,黑的全为黑了

thresh1 是图像的值,ret是阈值

THRESH_BINARY_INV 中INV表示反转

2.2 图像平滑

java 复制代码
#%% md
![image.png](attachment:image.png)
#%%
img = cv2.imread('../img/lenaNoise.png')

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

有些白点---》去掉

我们这里要用到均值滤波,意思就是把204变为周围3*3的平均值,每个值都这样处理

怎么计算呢,我们可以把这个33的矩阵去乘以33的全1矩阵

java 复制代码
# 均值滤波
# 简单的平均卷积操作
blur = cv2.blur(img, (3, 3))

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

发现白点没有那么明显了

java 复制代码
# 方框滤波
# 基本和均值一样,可以选择归一化
box = cv2.boxFilter(img,-1,(3,3), normalize=True)  

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

参数 -1 表示输出图像的深度(即像素值的数据类型)与输入图像保持一致。

当 normalize=True 时,滤波核(方框)内所有像素的权重会被平均化(总和为 1)。计算方式是:将方框内所有像素值相加后,再除以方框内像素的总数量(即方框面积),这样可以保证输出像素值不会超出原图像的像素值范围。

如果设置 normalize=False,则不进行归一化,此时计算方式只是简单地将方框内所有像素值相加。这种情况下,如果方框较大,可能会导致像素值超过最大值(如 255),从而出现图像过曝(白块)现象。

所以得到的结果和均值滤波一样

java 复制代码
# 方框滤波
# 基本和均值一样,可以选择归一化
box = cv2.boxFilter(img,-1,(3,3), normalize=False)

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

False就没有除以9了,直接超级白了

2.3 高斯与中值滤波

高斯滤波就是这样的,近的就占的比重大点

java 复制代码
# 高斯滤波
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(img, (5, 5), 1)  

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

中值滤波就是3*3数字排完序之后,取中间的值

java 复制代码
# 中值滤波
# 相当于用中值代替
median = cv2.medianBlur(img, 5)  # 中值滤波

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

5表示5*5

基本上都不见了

因为噪音点少,而且噪音点比较亮,值比较大,所以排序选中间一般可能选不到它

但是取平均的话,很有可能就会偏亮,因为噪音点值大

java 复制代码
# 展示所有的
res = np.hstack((blur,aussian,median))
print (res)
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

这个就是图像拼接,hstack表示横着拼接

vstack表示竖着拼接

3. 图像形态学操作

3.1 形态学-腐蚀操作

java 复制代码
img = cv2.imread('../img/dige.png')

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

这就是图像操作的前提,图像数据是二值的

意思就是只有两种颜色

怎么去掉那些虚线呢--》腐蚀操作

java 复制代码
pie = cv2.imread('../img/pie.png')

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

腐蚀操作意思就是当一个点的3*3范围内的时候,有两种颜色----》这个点就腐蚀掉-----》置于黑色

----》就会变小了,这个圆

腐蚀而且还有迭代次数的,每次腐蚀,圆都会变小

java 复制代码
kernel = np.ones((3,3),np.uint8) 
erosion = cv2.erode(img,kernel,iterations = 1)

cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()
java 复制代码
kernel = np.ones((3,3),np.uint8) 
erosion = cv2.erode(img,kernel,iterations = 3)

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

(3,3)这个区间越大,或者迭代次数越多,那么腐蚀的力度就越大

java 复制代码
kernel = np.ones((30,30),np.uint8) 
erosion_1 = cv2.erode(pie,kernel,iterations = 1)
erosion_2 = cv2.erode(pie,kernel,iterations = 2)
erosion_3 = cv2.erode(pie,kernel,iterations = 3)
res = np.hstack((erosion_1,erosion_2,erosion_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.2 形态学-膨胀操作

这个就是变大了,和腐蚀是相反的效果

java 复制代码
kernel = np.ones((3,3),np.uint8) 
dige_erosion = cv2.erode(img,kernel,iterations = 1)

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

怎么变胖一点呢

java 复制代码
kernel = np.ones((3,3),np.uint8) 
dige_dilate = cv2.dilate(dige_erosion,kernel,iterations = 1)

cv2.imshow('dilate', dige_dilate)
cv2.waitKey(0)
cv2.destroyAllWindows()

就是3*3的时候,有两种颜色的时候,变成白色

java 复制代码
pie = cv2.imread('../img/pie.png')

kernel = np.ones((30,30),np.uint8) 
dilate_1 = cv2.dilate(pie,kernel,iterations = 1)
dilate_2 = cv2.dilate(pie,kernel,iterations = 2)
dilate_3 = cv2.dilate(pie,kernel,iterations = 3)
res = np.hstack((dilate_1,dilate_2,dilate_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.3 开运算与闭运算

java 复制代码
# 开:先腐蚀,再膨胀
img = cv2.imread('../img/dige.png')

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

cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()
java 复制代码
# 闭:先膨胀,再腐蚀
img = cv2.imread('../img/dige.png')

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

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

所以不能去掉毛刺

3.4 梯度运算

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

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

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

这个是正常膨胀和腐蚀之后的结果

梯度运算就是膨胀-腐蚀,那么一相减的话,中间那部分相同的部分就变为黑色了

java 复制代码
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)

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

3.5 礼帽与黑帽

礼帽 = 原始输入-开运算结果(先腐蚀在膨胀)----》只剩下毛刺了

黑帽 = 闭运算(先膨胀在腐蚀)-原始输入 -----》原始的小轮廓了

java 复制代码
#礼帽
img = cv2.imread('../img/dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()
java 复制代码
#黑帽
img = cv2.imread('../img/dige.png')
blackhat  = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat ', blackhat )
cv2.waitKey(0)
cv2.destroyAllWindows()

4. 图像梯度运算

4.1 图像梯度-Sobel算子

java 复制代码
img = cv2.imread('../img/pie.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()

什么是梯度呢,梯度就是一个点上,有不同颜色,就是有梯度---》横着竖着,分别看有没有梯度

假设我们选的点是p5,然后

咋们的这个矩阵计算呢,就是对应位置相乘的计算,并不是正在的矩阵乘法

所以这个梯度就是右边的值减去左边的,而且近的点,比重大,*2

这样就对比出来了左边与右边的差异---》也就是左右的梯度Gx

竖着的梯度就是下边的减去上边的

java 复制代码
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)

ddepth:图像的深度,一般是-1就可以了,输出的深度和输入深度一样
dx和dy分别表示水平和竖直方向
ksize是Sobel算子的大小

如果Gx为负数那么就取0,因为这里的颜色数值都是大于等于0的,而且只有32位,只能表示0~255

java 复制代码
def cv_show(img,name):
    cv2.imshow(name,img)
    cv2.waitKey()
    cv2.destroyAllWindows()
java 复制代码
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)

cv_show(sobelx,'sobelx')

cv2.CV_64F,使用 64 位浮点型是为了能够表示负数值(

这样梯度为负数的时候,也是直接表示为负数了,不会表示为0了

dx=1,dy-0表示只算水平的梯度,不算竖直的梯度

因为算的是水平的,是右边减去左边,那么偏左的时候,白色-黑色是大于0,而且是白色,值不变,因为黑色是0

偏右的时候,黑色减去左边小于0,为-255,负数不能显示出来---》截断--》黑色

java 复制代码
白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值
java 复制代码
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
cv_show(sobelx,'sobelx')

convertScaleAbs这个就是取绝对值了,因为CV_64F,所以里面存的是负数,再取绝对值,就对了

这个就是总的梯度计算

或者Gx的绝对值+Gy的绝对值

java 复制代码
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)  
cv_show(sobely,'sobely')
java 复制代码
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')

就是0.5Gx+0.5Gy了

java 复制代码
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy = cv2.convertScaleAbs(sobelxy) 
cv_show(sobelxy,'sobelxy')

一开始的时候,就一起计算呢

所以说这就是为什么东南西北四个方向都没有白点的原因

不建议直接计算

java 复制代码
img = cv2.imread('../img/lena.jpg',cv2.IMREAD_GRAYSCALE)
cv_show(img,'img')

这是一个灰度图

java 复制代码
img = cv2.imread('../img/lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')

就是找出轮廓的作用了

java 复制代码
# 不建议直接计算

img = cv2.imread('../img/lena.jpg',cv2.IMREAD_GRAYSCALE)

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

我们发现直接计算就效果不好了,直接计算会少很多的点

4.2 图像梯度-Scharr算子和laplacian算子

发现这个差值更大了,但还是右边减去左边,下边减去上边

这个是中间点和边缘点的差值

如果这个点不在边界-----》变黑

在边界-------》有变化了

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

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

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

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

这个就是上面讲的三个算法

第二个描述得更细节了,你看那木头的纹路都弄出来了

Laplacian描述得一般

总结

相关推荐
liuyunshengsir2 分钟前
PyTorch 动态量化(Dynamic Quantization)
人工智能·pytorch·python
电子云与长程纠缠11 分钟前
UE5制作六边形包裹球体效果
开发语言·python·ue5
DFT计算杂谈20 分钟前
KPROJ编译教程
java·前端·python·算法·conda
念恒123061 小时前
Python(循环中断)
开发语言·python
tsfy20031 小时前
Python 处理中文文件名的3个坑(附 Flask 上传解决函数)
开发语言·python·flask·文件上传·中文编码
AI技术控1 小时前
KV Cache 缓存机制的原理和应用:从 Transformer 推理到大模型服务优化
人工智能·python·深度学习·缓存·自然语言处理·transformer
vx-程序开发2 小时前
基于机器学习的动漫可视化系统的设计与实现-计算机毕业设计源码08339
java·c++·spring boot·python·spring·django·php
J&A~ing2 小时前
第一章 opencv 的 Windows源码在 Visual Studio 下的编译安装
人工智能·windows·opencv·计算机视觉·visual studio
爱睡懒觉的焦糖玛奇朵2 小时前
【从视频到数据集:焦糖玛奇朵的魔法工具Video To YOLO Dataset】
人工智能·python·学习·yolo·音视频
石榴树下的七彩鱼2 小时前
医疗票据 OCR 识别 API 多场景落地指南:医保结算 + 商保理赔 + 医疗信息化(附 Python/Java 完整示例)
java·python·ocr·石榴智能·医疗票据ocr·医保结算·ocrapi