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描述得一般

总结

相关推荐
xiangzhihong82 小时前
Spring Boot实现文字转语音功能
开发语言·python
天才测试猿4 小时前
postman使用总结
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
可触的未来,发芽的智生4 小时前
新奇特:神经网络烘焙坊(下),万能配方的甜蜜奥义
人工智能·python·神经网络·算法·架构
计算机毕业设计指导4 小时前
基于Django的内部网络资产发现与管理工具
网络·python·网络安全·django
程序员大辉4 小时前
python代码案例分享,python实现代码雨,动画显示,pygame使用教程
开发语言·python
winfredzhang4 小时前
python图片处理与PDF生成程序详解
python·pdf·图片·解压
伊织code5 小时前
python-poppler - PDF文档处理Python绑定库
开发语言·python·pdf·python-poppler
SunnyDays10115 小时前
Python 高效将 PDF 转换为 HTML 的实用指南
python·pdf转html
Q_Q5110082856 小时前
python+django/flask在线问诊系统 医院就诊 医生推荐系统
spring boot·python·django·flask·node.js·php