OpenCV 图像梯度计算(学习笔记) 电脑版发表于:2024/1/9 14:25 ![](https://img.tnblog.net/arcimg/hb/3c9a034e352c4476b4ec6d8cc07a0263.png) >#OpenCV 图像梯度计算(学习笔记) [TOC] ## Sobel算子简介 tn2>Sobel算子是一个图像边缘检测的算法。 其基本原理是通过计算像素点梯度值来确定图像中的边缘信息。 ### 什么情况下会产生梯度? tn2>举例:在一个白心圆黑心背景图中,纯白或纯黑部分都不会产生,只有的边缘部分将会产生梯度的情况。 ![](https://img.tnblog.net/arcimg/hb/976098752b4b45c6b049e3ac0e4a8578.png) ### 定义Sobel算子模板 tn2>Sobel算子模板是一个`3*3`的矩阵卷积核。 将`3*3`的模板矩阵与图像中的某个像素点对应位置进行对齐。 如下图所示: ![](https://img.tnblog.net/arcimg/hb/fd4e408f31444cf28625f5d76590345a.png) tn2>当我们要求出模板矩阵与该像素点周围8个像素点的加权平均值,水平`x`方向为: ![](https://img.tnblog.net/arcimg/hb/fb894e7c919f4a3794044df8407f8fde.png) tn>含义:当目标(P5点)左右两列差别特别大的时候,目标点的值会很大,说明该点为边界。 tn2>求`y`方向的权值公式如下: ![](https://img.tnblog.net/arcimg/hb/ce157c76fc494c5dbbe17e6c19e113a0.png) ### Sobel算子的梯度问题 tn2>1.目标像素点求得的值小于0或者大于255怎么办? 通常Opencv默认的是是截断操作,即小于0就按0算,大于255按255算。 2.截断操作合适吗? 不合适,因为呈现的程度不一样。举例:-200或-1它都是按照0来算 3.应该如何操作? 对于小于0的值取出绝对值,大于255的可按255算(因为255是最大的值了) ### 总梯度计算 ![](https://img.tnblog.net/arcimg/hb/048e4b3c9ad64b13947f524e619d1338.png) ### 示例代码 ```python import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline ``` tn2>展示原图。 ```python img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE) cv2.imshow("img",img) cv2.waitKey() cv2.destroyAllWindows() ``` ![](https://img.tnblog.net/arcimg/hb/471585fefb7f4982ad2caf56906d6312.png) tn2>可通过`cv2.Sobel`来进行计算,举例:`dst = cv2.Sobel(src, ddepth, dx, dy, ksize)`参数如下: `ddepth`:图像的深度 `dx`和`dy`分别表示水平和竖直方向 `ksize`是Sobel算子的大小 ```python def cv_show(img,name): cv2.imshow(name,img) cv2.waitKey() cv2.destroyAllWindows() ``` ```python sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) cv_show(sobelx,'sobelx') ``` ![](https://img.tnblog.net/arcimg/hb/ffb7202819304649ad7350813a6f3fa9.png) tn2>白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值。 ```python sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) # 进行取绝对值操作 sobelx = cv2.convertScaleAbs(sobelx) cv_show(sobelx,'sobelx') ``` ![](https://img.tnblog.net/arcimg/hb/feed1cf8452145bd8908adde5ab03cb5.png) tn2>再求`y`的Sobel算子。 ![](https://img.tnblog.net/arcimg/hb/0ca976cfc5734d54b1be7d9faf9bf64b.png) tn2>分别计算x和y,再求和. ```python sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) cv_show(sobelxy,'sobelxy') ``` ![](https://img.tnblog.net/arcimg/hb/ecfddcc97dc4421e93b7c47ad8b514f9.png) tn2>不建议直接计算. ```python sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3) sobelxy = cv2.convertScaleAbs(sobelxy) cv_show(sobelxy,'sobelxy') ``` ![](https://img.tnblog.net/arcimg/hb/dd03fd65b28f46bbb7ebc3f9c2eed7fa.png) tn2>再举个例子。 ```python img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE) cv_show(img,'img') ``` ![](https://img.tnblog.net/arcimg/hb/1359e78e06b849f396b9297bea6bea62.png) ```python img = cv2.imread('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') ``` ![](https://img.tnblog.net/arcimg/hb/0a2a0581a0b14ebfbe98fa72ae326f76.png) tn2>但是如果直接计算就会效果不佳。 ```python img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE) sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3) sobelxy = cv2.convertScaleAbs(sobelxy) cv_show(sobelxy,'sobelxy') ``` ![](https://img.tnblog.net/arcimg/hb/41882c14eab649b3b03bf83837a206e3.png) ## Scharr算子 tn2>与Sobel算子一样的原理,只是数值比它大一些,对于细节刻画得更明显一些。 ![](https://img.tnblog.net/arcimg/hb/5d0f51a0d2574ee6ae4bb576e0600ea6.png) ## laplacian算子 tn2>这个很不一样,不会进行梯度。 Laplacian是利用二阶导数来检测边缘,因为图像是二维的,我们需要在两个方向上求导,如下式所示: ![](https://img.tnblog.net/arcimg/hb/25a3b7661dcd48379a18dc9a55a2110d.png) ![](https://img.tnblog.net/arcimg/hb/b9caac1668b642929b16fc14feaffceb.png) tn2>计算P5的梯度公式如下: $$\large P5{_n}ew=(P2+P4+P6+P8)-4*P5 $$ ## 代码实验对比 tn2>通过进行Sobel、Scharr和laplacian的代码如下所示: ```python #不同算子的差异 img = cv2.imread('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') ``` ![](https://img.tnblog.net/arcimg/hb/c164b547a6884d1a9b2ceb4d33537ea9.png)