精华内容
下载资源
问答
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...

    前文传送门:

    「Python 图像处理 OpenCV (1):入门」

    「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」

    「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」

    「Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间」

    「Python 图像处理 OpenCV (5):图像的几何变换」

    「Python 图像处理 OpenCV (6):图像的阈值处理」

    「Python 图像处理 OpenCV (7):图像平滑(滤波)处理」

    「Python 图像处理 OpenCV (8):图像腐蚀与图像膨胀」

    「Python 图像处理 OpenCV (9):图像处理形态学开运算、闭运算以及梯度运算」

    「Python 图像处理 OpenCV (10):图像处理形态学之顶帽运算与黑帽运算」

    「Python 图像处理 OpenCV (11):Canny 算子边缘检测技术」

    「Python 图像处理 OpenCV (12): Roberts 算子、 Prewitt 算子、 Sobel 算子和 Laplacian 算子边缘检测技术」

    「Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术」

    引言

    前面的文章中,我们有用过图像方法或者缩小的函数 resize() ,这个函数既可以放大图像,也可以缩小图像,其中:

    • 缩小图像:一版使用 CV_INETR_AREA (区域插值)来插值。
    • 放大图像,一般使用 CV_INTER_LINEAR (线性插值)来插值。

    图像缩放除了可以使用函数 resize() ,还有另外的一种方式 —— 「图像金字塔」。

    图像金字塔是什么?

    在说清楚什么事图像金字塔之前,要先介绍另一个概念:「尺度」。

    尺度:先从字面意思来看说的就是尺寸和分辨率。

    我们在进行图像处理的时候,会经常对源图像的尺寸进行放大或者缩小的变换,进而转换为我们需要的尺寸的目标图像。

    对图像进行放大和缩小的变换的这个过程,称为尺度调整。

    而图像金字塔则是图像多尺度调整表达的一种重要的方式。

    图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

    图像金字塔方法的总体思想主要是是:将参加融合的的每幅图像分解为多尺度的金字塔图像序列,将低分辨率的图像在上层,高分辨率的图像在下层,上层图像的大小为前一层图像大小的 1/4 。层数为 0 , 1 , 2 …… N 。将所有图像的金字塔在相应层上以一定的规则融合,就可得到合成金字塔,再将该合成金字塔按照金字塔生成的逆过程进行重构,得到融合金字塔。

    实现方式

    通常而言,我们一般讨论两种图像金字塔:「高斯金字塔( Gaussian pyramid )」 和 「拉普拉斯金字塔( Laplacian pyramid )」 。

    高斯金字塔( Gaussian pyramid )

    高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像。最下面的图像分辨率最高,越往上图像分辨率越低。

    高斯金字塔向下采样:

    这个过程实际上就是一个重复高斯平滑并重新对图像采样的过程。

    1. 对于原始图像先进行一次高斯平滑处理,使用高斯核(5 * 5)进行一次卷积处理。下面是 5 * 5 的高斯核。

    K=1125[1464141624164624362464162416414641] K = \frac{1}{125} \left[ \begin{matrix} 1 & 4 & 6 & 4 & 1\\ 4 & 16 & 24 & 16 & 4\\ 6 & 24 & 36 & 24 & 6\\ 4 & 16 & 24 & 16 & 4\\ 1 & 4 & 6 & 4 & 1\\ \end{matrix} \right]

    1. 接下来是对图像进行采样,这一步会去除图像中的偶数行和奇数列,从而得到一张图像。
    2. 再然后是重复上面两步,直到得到最终的目标图像为止。

    从上面的步骤可以看出,再每次循环中,得到的结果图像只有原图像的 1/4 大小(横纵向均做隔行采样)。

    注意:向下采样会逐渐丢失图像信息,属于非线性的处理,此过程不可逆,属于有损处理。

    高斯金字塔向上采样:

    1. 将图像在每个方向扩大为原来的两倍,新增的行和列以 0 填充。
    2. 使用高斯核(5 * 5)对得到的图像进行一次高斯平滑处理,获得 「新增像素」的近似值。

    注意:此过程与向下采样的过程一样,属于非线性处理,无法逆转,属于有损处理。

    此过程得到的图像为放大后的图像,与原图相比会比较模糊,因为在缩放的过程中丢失了一些图像信息,如果想在缩小和放大整个过程中减少信息的丢失。

    如果在缩放过程中想要减少图像信息的丢失,这就引出了第二个图像金字塔 —— 「拉普拉斯金字塔」 。

    拉普拉斯金字塔( Laplacian pyramid )

    拉普拉斯金字塔可以认为是残差金字塔,用来存储下采样后图片与原始图片的差异。

    上面我们介绍了基于高斯金字塔,一个原始图像 Gi ,先进行向下采样得到 G(i-1) ,再对 G(i-1) 进行向上采样得到 Up(Down(Gi)) ,最终得到的 Up(Down(Gi)) 与原始的 Gi 是存在差异的。

    这是因为向下采样丢失的信息并不能由向上采样来进行恢复,高斯金字塔是一种有损的采样方式。

    如果我们想要完全恢复原始图像,那么我们在进行采样的时候就需要保留差异信息。

    这就是拉普拉斯金字塔的核心思想,每次向下采样后,将再次向上采样,得到向上采样的 Up(Down(Gi)) 后,记录 Up(Down(Gi))Gi 的差异信息。

    下面这个公式是差异的记录过程:

    Li=GiUp(Down(Gi)) L_i = G_i - Up(Down(G_i))

    OpenCV 函数

    OpenCV 为向上采样和向下采样提供了两个函数: pyrDown()pyrUp()

    pyrDown() 的原函数如下:

    def pyrDown(src, dst=None, dstsize=None, borderType=None)
    
    • src: 表示输入图像。
    • dst: 表示输出图像,它与src类型、大小相同。
    • dstsize: 表示降采样之后的目标图像的大小。
    • borderType: 表示表示图像边界的处理方式。

    注意:dstsize 参数是有默认值的,调用函数的时候不指定第三个参数,那么这个值是按照 Size((src.cols+1)/2, (src.rows+1)/2) 计算的。而且不管如何指定这个参数,一定必须保证满足以下关系式:|dstsize.width * 2 - src.cols| ≤ 2; |dstsize.height * 2 - src.rows| ≤ 2。也就是说降采样的意思其实是把图像的尺寸缩减一半,行和列同时缩减一半。

    pyrUp() 的原函数如下:

    def pyrUp(src, dst=None, dstsize=None, borderType=None)
    
    • src: 表示输入图像。
    • dst: 表示输出图像,它与src类型、大小相同。
    • dstsize: 表示降采样之后的目标图像的大小。
    • borderType: 表示表示图像边界的处理方式。

    参数释义和上面的 pyrDown() 保持一致。

    下面是高斯金字塔和拉普拉斯金字塔的代码示例:

    import cv2 as cv
    
    #高斯金字塔
    def gaussian_pyramid(image):
        level = 3      #设置金字塔的层数为3
        temp = image.copy()  #拷贝图像
        gaussian_images = []  #建立一个空列表
        for i in range(level):
            dst = cv.pyrDown(temp)   #先对图像进行高斯平滑,然后再进行降采样(将图像尺寸行和列方向缩减一半)
            gaussian_images.append(dst)  #在列表末尾添加新的对象
            cv.imshow("gaussian"+str(i), dst)
            temp = dst.copy()
        return gaussian_images
    
    
    #拉普拉斯金字塔
    def laplacian_pyramid(image):
        gaussian_images = gaussian_pyramid(image)    #做拉普拉斯金字塔必须用到高斯金字塔的结果
        level = len(gaussian_images)
        for i in range(level-1, -1, -1):
            if (i-1) < 0:
                expand = cv.pyrUp(gaussian_images[i], dstsize = image.shape[:2])
                laplacian = cv.subtract(image, expand)
                # 展示差值图像
                cv.imshow("laplacian_down_"+str(i), laplacian)
            else:
                expand = cv.pyrUp(gaussian_images[i], dstsize = gaussian_images[i-1].shape[:2])
                laplacian = cv.subtract(gaussian_images[i-1], expand)
                # 展示差值图像
                cv.imshow("laplacian_down_"+str(i), laplacian)
    
    
    src = cv.imread('maliao.jpg')
    print(src.shape)
    # 先将图像转化成正方形,否则会报错
    input_image = cv.resize(src, (560, 560))
    # 设置为 WINDOW_NORMAL 可以任意缩放
    cv.namedWindow('input_image', cv.WINDOW_AUTOSIZE)
    cv.imshow('input_image', src)
    laplacian_pyramid(src)
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    上面这段程序有一点需要注意,我当前使用 opencv-python 的版本是 4.3.0.36 ,理论上在向上采样的过程中,目标大小只需要满足关系 |dstsize.width - src.cols * 2| ≤ (dstsize.width mod 2) 即可。

    实际上经过测试,输入图像是必须使用正方形,长方形的图像会直接爆出如下错误:

    error: (-215:Assertion failed) std::abs(dsize.width - ssize.width*2) == dsize.width % 2 && std::abs(dsize.height - ssize.height*2) == dsize.height % 2 in function 'cv::pyrUp_'
    

    具体原因并没有想通,希望哪位知道的大佬可以解释下。

    参考

    https://blog.csdn.net/zhu_hongji/article/details/81536820

    https://zhuanlan.zhihu.com/p/80362140

    您的扫码关注,是对小编坚持原创的最大鼓励:)
    展开全文
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...

    前文传送门:

    「Python 图像处理 OpenCV (1):入门」

    「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」

    「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」

    「Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间」

    「Python 图像处理 OpenCV (5):图像的几何变换」

    「Python 图像处理 OpenCV (6):图像的阈值处理」

    1. 引言

    第一件事情还是先做名词解释,图像平滑到底是个啥?

    从字面意思理解貌似图像平滑好像是在说图像滑动。

    emmmmmmmmmmmmmmm。。。。

    其实半毛钱关系也没有,图像平滑技术通常也被成为图像滤波技术(这个名字看到可能大家会有点感觉)。

    每一幅图像都包含某种程度的噪声,噪声可以理解为由一种或者多种原因造成的灰度值的随机变化,如由光子通量的随机性造成的噪声等等。

    而图像平滑技术或者是图像滤波技术就是用来处理图像上的噪声,其中,能够具备边缘保持作用的图像平滑处理,成为了大家关注的重点。

    这不废话,处理个图片降噪,结果把整个图像搞的跟玻璃上糊上了一层水雾一样,这种降噪有啥意义。

    本文会介绍 OpenCV 中提供的图像平滑的 4 个算法:

    • 均值滤波
    • 方框滤波
    • 高斯滤波
    • 中值滤波

    下面开始一个一个看吧:)

    先给出一个给马里奥加噪声的程序,程序来源于杨老师的博客:https://blog.csdn.net/Eastmount/article/details/82216380 ,完整代码如下:

    import cv2 as cv
    import numpy as np
    
    # 读取图片
    img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
    rows, cols, chn = img.shape
    
    # 加噪声
    for i in range(5000):
        x = np.random.randint(0, rows)
        y = np.random.randint(0, cols)
        img[x, y, :] = 255
    
    cv.imshow("noise", img)
    
    # 图像保存
    cv.imwrite("maliao_noise.jpg", img)
    
    # 等待显示
    cv.waitKey()
    cv.destroyAllWindows()
    

    上面这段程序实际上是在图片上随机加了 5000 个白点,这个噪声真的是够大的了。

    2. 2D 图像卷积

    在介绍滤波之前先简单介绍下 2D 图像卷积,图像卷积其实就是图像过滤。

    图像过滤的时候可以使用各种低通滤波器( LPF ),高通滤波器( HPF )等对图像进行过滤。

    低通滤波器( LPF )有助于消除噪声,但是会使图像模糊。

    高通滤波器( HPF )有助于在图像中找到边缘。

    OpenCV 为我们提供了一个函数 filter2D() 来将内核与图像进行卷积。

    我们尝试对图像进行平均滤波, 5 x 5 平均滤波器内核如下:

    K=125[1111111111111111111111111] K = \frac{1}{25} \begin{bmatrix} 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 \end{bmatrix}

    具体操作如下:

    我们保持这个内核在一个像素上,将所有低于这个内核的 25 个像素相加,取其平均值,然后用新的平均值替换中心像素。它将对图像中的所有像素继续此操作,完整的示例代码如下:

    import numpy as np
    import cv2 as cv
    from matplotlib import pyplot as plt
    
    # 读取图片
    img = cv.imread("maliao_noise.jpg", cv.IMREAD_UNCHANGED)
    rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
    
    kernel = np.ones((5,5),np.float32)/25
    
    dst = cv.filter2D(rgb_img, -1, kernel)
    
    titles = ['Source Image', 'filter2D Image']
    images = [rgb_img, dst]
    
    for i in range(2):
        plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    
    plt.show()
    

    可以看到,噪点确实去除掉了,就是图片变得模糊起来。

    3. 均值滤波

    均值滤波是指任意一点的像素值,都是周围 N * M 个像素值的均值。

    其实均值滤波和上面的那个图像卷积的示例,做了同样的事情,我只是用 filter2D() 这个方法手动完成了均值滤波,实际上 OpenCV 为我们提供了专门的均值滤波的方法,前面图像卷积没有看明白的同学,可以再一遍均值滤波,我尽量把这个事情整的明白的。

    还是来画个图吧:

    中间那个红色的方框里面的值,是周围 25 个格子区域中的像素的和去除以 25 ,这个公式是下面这样的:

    K=125[1111111111111111111111111] K = \frac{1}{25} \begin{bmatrix} 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 \end{bmatrix}

    我为了偷懒,所有的格子里面的像素值都写成 1 ,毕竟 n / n 永远都等于 1 ,快夸我机智。

    上面这个 5 * 5 的矩阵称为核,针对原始图像内的像素点,采用核进行处理,得到结果图像。

    这个核我们可以自定义大小,比如 5 * 5 ,3 * 3 , 10 * 10 等等,具体定义多大完全看疗效。

    OpenCV 为我提供了 blur() 方法用作实现均值滤波,原函数如下:

    def blur(src, ksize, dst=None, anchor=None, borderType=None)
    
    • kSize: 内核参数,其实就是图片进行卷积的时候相乘的那个矩阵,具体的卷积是如何算的,网上有很多,我这里就不介绍了,所得到的图像是模糊的,而且图像其实是按照原来的比例缺少了(原图像-内核参数+1)^2 个单元格。
    • anchor: Point 类型,即锚点,有默认值 Point(-1, -1) ,当坐标为负值,就表示取核的中心。
    • borderType: Int 类型,用于推断图像外部像素的某种边界模式,有默认值 BORDER_DEFAULT 。

    接下来是均值滤波的示例代码:

    import cv2 as cv
    import matplotlib.pyplot as plt
    
    # 读取图片
    img = cv.imread("maliao_noise.jpg", cv.IMREAD_UNCHANGED)
    rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
    
    # 均值滤波
    blur_img = cv.blur(rgb_img, (3, 3))
    # blur_img = cv.blur(img, (5, 5))
    # blur_img = cv.blur(img, (10, 10))
    # blur_img = cv.blur(img, (20, 20))
    
    titles = ['Source Image', 'Blur Image']
    images = [rgb_img, blur_img]
    
    for i in range(2):
        plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    
    plt.show()
    

    这个降噪的效果好像没有前面 2D 卷积的那个降噪效果好,但是图像更为清晰,因为我在这个示例中使用了更小的核 3 * 3 的核,顺便我也试了下大核,比如代码中注释掉的 10 * 10 的核或者 20 * 20 的核,实时证明,核越大降噪效果越好,但是相反的是图像会越模糊。

    4. 方框滤波

    方框滤波和均值滤波核基本一致,其中的区别是需不需要进行归一化处理。

    什么是归一化处理等下再说,我们先看方框滤波的原函数:

    def boxFilter(src, ddepth, ksize, dst=None, anchor=None, normalize=None, borderType=None)
    
    • src: 原始图像。
    • ddepth: Int 类型,目标图像深度,通常用 -1 表示与原始图像一致。
    • kSize: 内核参数。
    • dst: 输出与 src 大小和类型相同的图像。
    • anchor: Point 类型,即锚点,有默认值 Point(-1, -1) 。
    • normalize: Int 类型,表示是否对目标图像进行归一化处理。

    当 normalize 为 true 时,需要执行均值化处理。

    当 normalize 为 false 时,不进行均值化处理,实际上是求周围各像素的和,很容易发生溢出,溢出时均为白色,对应像素值为 255 。

    完整示例代码如下:

    import cv2 as cv
    import matplotlib.pyplot as plt
    
    # 读取图片
    img = cv.imread('maliao_noise.jpg')
    source = cv.cvtColor(img, cv.COLOR_BGR2RGB)
    
    # 方框滤波
    result = cv.boxFilter(source, -1, (5, 5), normalize = 1)
    
    # 显示图形
    titles = ['Source Image', 'BoxFilter Image']
    images = [source, result]
    
    for i in range(2):
        plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    
    plt.show()
    

    当我们把 normalize 的属性设为 0 时,不进行归一化处理,结果就变成了下面这个样子:

    5. 高斯滤波

    为了克服简单局部平均法的弊端(图像模糊),目前已提出许多保持边缘、细节的局部平滑算法。它们的出发点都集中在如何选择邻域的大小、形状和方向、参数加平均及邻域各店的权重系数等。

    在高斯滤波的方法中,实际上是把卷积核换成了高斯核,那么什么是高斯核呢?

    简单来讲就是方框还是那个方框,原来每个方框里面的权是相等的,大家最后取平均,现在变成了高斯分布的,方框中心的那个权值最大,其余方框根据距离中心元素的距离递减,构成一个高斯小山包,这样取到的值就变成了加权平均。

    下图是所示的是 3 * 3 和 5 * 5 领域的高斯核。

    高斯滤波是在 OpenCV 中是由 GaussianBlur() 方法进行实现的,它的原函数如下:

    def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
    
    • sigmaX: 表示 X 方向方差。

    这里需要注意的是 ksize 核大小,在高斯核当中,核 (N, N) 必须是奇数, X 方向方差主要控制权重。

    完整的示例代码如下:

    import cv2 as cv
    import matplotlib.pyplot as plt
    
    # 读取图片
    img = cv.imread('maliao_noise.jpg')
    source = cv.cvtColor(img, cv.COLOR_BGR2RGB)
    
    # 方框滤波
    result = cv.GaussianBlur(source, (3, 3), 0)
    
    # 显示图形
    titles = ['Source Image', 'GaussianBlur Image']
    images = [source, result]
    
    for i in range(2):
        plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    
    plt.show()
    

    6. 中值滤波

    在使用邻域平均法去噪的同时也使得边界变得模糊。

    而中值滤波是非线性的图像处理方法,在去噪的同时可以兼顾到边界信息的保留。

    中值滤波具体的做法是选一个含有奇数点的窗口 W ,将这个窗口在图像上扫描,把窗口中所含的像素点按灰度级的升或降序排列,取位于中间的灰度值来代替该点的灰度值。

    下图是一个一维的窗口的滤波过程:

    在 OpenCV 中,主要是通过调用 medianBlur() 来实现中值滤波,它的原函数如下:

    def medianBlur(src, ksize, dst=None)
    

    中值滤波的核心数和高斯滤波的核心数一样,必须要是大于 1 的奇数。

    示例代码如下:

    import cv2 as cv
    import matplotlib.pyplot as plt
    
    # 读取图片
    img = cv.imread('maliao_noise.jpg')
    source = cv.cvtColor(img, cv.COLOR_BGR2RGB)
    
    # 方框滤波
    result = cv.medianBlur(source, 3)
    
    # 显示图形
    titles = ['Source Image', 'medianBlur Image']
    images = [source, result]
    
    for i in range(2):
        plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    
    plt.show()
    

    可以明显看到,目前中值滤波是对原图像降噪后还原度最高的,常用的中值滤波的图形除了可以使用方框,还有十字形、圆形和环形,不同形状的窗口产生不同的滤波效果。

    方形和圆形窗口适合外轮廓线较长的物体图像,而十字形窗口对有尖顶角状的图像效果好。

    对于一些细节较多的复杂图像,可以多次使用不同的中值滤波。

    7. 示例代码

    如果有需要获取源码的同学可以在公众号回复「OpenCV」进行获取。

    8. 参考

    https://blog.csdn.net/Eastmount/article/details/82216380

    http://www.woshicver.com/

    您的扫码关注,是对小编坚持原创的最大鼓励:)
    展开全文
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...

    前文传送门:

    「Python 图像处理 OpenCV (1):入门」

    「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」

    「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」

    「Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间」

    「Python 图像处理 OpenCV (5):图像的几何变换」

    「Python 图像处理 OpenCV (6):图像的阈值处理」

    「Python 图像处理 OpenCV (7):图像平滑(滤波)处理」

    「Python 图像处理 OpenCV (8):图像腐蚀与图像膨胀」

    「Python 图像处理 OpenCV (9):图像处理形态学开运算、闭运算以及梯度运算」

    「Python 图像处理 OpenCV (10):图像处理形态学之顶帽运算与黑帽运算」

    「Python 图像处理 OpenCV (11):Canny 算子边缘检测技术」

    「Python 图像处理 OpenCV (12): Roberts 算子、 Prewitt 算子、 Sobel 算子和 Laplacian 算子边缘检测技术」

    「Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术」

    「Python 图像处理 OpenCV (14):图像金字塔」

    引言

    其实蛮不好意思的,刚才翻了翻自己的博客,上次写 OpenCV 的文章已经接近半个月以前了,我用 3 秒钟的时间回想了下最近两星期时间都花在哪了。

    每次思考这种问题总会下意识甩锅给工作,最近工作忙的一批,emmmmmmmmmmmm。。。。。。。。。

    这么骗自己是不对的!

    实际上是美剧真香,最近把「反击」从第一季到第六季看了一遍,还不错,喜欢看动作类的同学可以尝试下。

    本篇文章是关于图像处理轮廓方面的,下面开始正文,希望能帮到各位。

    Q:什么是轮廓?

    A:轮廓是一系列相连的点组成的曲线,代表了物体的基本外形,相对于边缘,轮廓是连续的,边缘并不全部连续。

    寻找轮廓

    寻找轮廓 OpenCV 为我们提供了一个现成的函数 findContours()

    在 OpenCV 中,轮廓提取函数 findContours() 实现的是 1985 年由一名叫做 Satoshi Suzuki 的人发表的一篇论文中的算法,如下:

    Satoshi Suzuki and others. Topological structural analysis of digitized binary images by border following. Computer Vision, Graphics, and Image Processing, 30(1):32–46, 1985.

    对原理感兴趣的同学可以去搜搜看,不是很难理解。

    先看一个示例代码:

    import cv2 as cv
    
    img = cv.imread("black.png")
    gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 降噪
    ret, thresh = cv.threshold(gray_img, 127, 255, 0)
    # 寻找轮廓
    contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    
    print(len(contours[0]))
    

    这段代码先用 threshold() 对图像进行降噪处理,它的原型函数如下:

    retval, dst = cv.threshold(src, thresh, maxval, type[, dst] ) 
    
    • dst:结果图像。
    • src:原图像。
    • thresh:当前阈值。
    • maxVal:最大阈值,一般为255。
    • type:阈值类型,可选值如下:
    enum ThresholdTypes {
        THRESH_BINARY     = 0,  # 大于阈值的部分被置为 255 ,小于部分被置为 0
        THRESH_BINARY_INV = 1,  # 大于阈值部分被置为 0 ,小于部分被置为 255
        THRESH_TRUNC      = 2,  # 大于阈值部分被置为 threshold ,小于部分保持原样
        THRESH_TOZERO     = 3,  # 小于阈值部分被置为 0 ,大于部分保持不变
        THRESH_TOZERO_INV = 4,  # 大于阈值部分被置为 0 ,小于部分保持不变
        THRESH_OTSU       = 8,  # 自动处理,图像自适应二值化,常用区间 [0,255]
    };          
    

    查找轮廓使用的函数为 findContours() ,它的原型函数如下:

    cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  
    
    • image:源图像。
    • mode:表示轮廓检索模式。
    cv2.RETR_EXTERNAL 表示只检测外轮廓。
    cv2.RETR_LIST 检测的轮廓不建立等级关系。
    cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
    cv2.RETR_TREE 建立一个等级树结构的轮廓。
    
    • method:表示轮廓近似方法。
    cv2.CHAIN_APPROX_NONE 存储所有的轮廓点。
    cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。
    

    这里可以使用 print(len(contours[0])) 函数将包含的点的数量打印出来,比如在上面的示例中,使用参数 cv2.CHAIN_APPROX_NONE 轮廓点有 1382 个,而使用参数 cv2.CHAIN_APPROX_SIMPLE 则轮廓点只有 4 个。

    绘制轮廓

    绘制轮廓使用到的 OpenCV 为我们提供的 drawContours() 这个函数,下面是它的三个简单的例子:

    # To draw all the contours in an image:
    cv2.drawContours(img, contours, -1, (0,255,0), 3)
    # To draw an individual contour, say 4th contour:
    cv2.drawContours(img, contours, 3, (0,255,0), 3)
    # But most of the time, below method will be useful:
    cnt = contours[4]
    cv2.drawContours(img, [cnt], 0, (0,255,0), 3)
    

    drawContours() 函数中有五个参数:

    • 第一个参数是源图像。
    • 第二个参数是应该包含轮廓的列表。
    • 第三个参数是列表索引,用来选择要绘制的轮廓,为-1时表示绘制所有轮廓。
    • 第四个参数是轮廓颜色。
    • 第五个参数是轮廓线的宽度,为 -1 时表示填充。

    我们接着前面的示例把使用 findContours() 找出来的轮廓绘制出来:

    import cv2 as cv
    
    img = cv.imread("black.png")
    gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    cv.imshow("img", img)
    # 降噪
    ret, thresh = cv.threshold(gray_img, 127, 255, 0)
    # 寻找轮廓
    contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    
    print(len(contours[0]))
    
    # 绘制绿色轮廓
    cv.drawContours(img, contours, -1, (0,255,0), 3)
    
    cv.imshow("draw", img)
    
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    特征矩

    特征矩可以帮助我们计算一些图像的特征,例如物体的质心,物体的面积等,使用的函数为 moments()

    moments() 函数会将计算得到的矩以字典形式返回。

    import cv2 as cv
    
    img = cv.imread("number.png")
    
    gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 降噪
    ret, thresh = cv.threshold(gray_img, 127, 255, 0)
    # 寻找轮廓
    contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    
    cnt = contours[0]
    # 获取图像矩
    M = cv.moments(cnt)
    print(M)
    
    # 质心
    cx = int(M['m10'] / M['m00'])
    cy = int(M['m01'] / M['m00'])
    
    print(f'质心为:[{cx}, {cy}]')
    

    这时,我们取得了这个图像的矩,矩 M 中包含了很多轮廓的特征信息,除了示例中展示的质心的计算,还有如 M[‘m00’] 表示轮廓面积。

    轮廓面积

    area = cv.contourArea(cnt)
    print(f'轮廓面积为:{area}')
    

    这里取到的轮廓面积和上面 M[‘m00’] 保持一致。

    轮廓周长

    perimeter = cv.arcLength(cnt, True)
    print(f'轮廓周长为:{perimeter}')
    

    参数 True 表示轮廓是否封闭,我们这里的轮廓是封闭的,所以这里写 True

    轮廓外接矩形

    轮廓外接矩形分为正矩形和最小矩形。使用 cv2.boundingRect(cnt) 来获取轮廓的外接正矩形,它不考虑物体的旋转,所以该矩形的面积一般不会最小;使用 cv.minAreaRect(cnt) 可以获取轮廓的外接最小矩形。

    两者的区别如上图,绿线代表的是外接正矩形,红线代表的是外接最小矩形,代码如下:

    import cv2 as cv
    import numpy as np
    
    img = cv.imread("number.png")
    
    gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 降噪
    ret, thresh = cv.threshold(gray_img, 127, 255, 0)
    # 寻找轮廓
    contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    
    cnt = contours[0]
    
    # 外接正矩形
    x, y, w, h = cv.boundingRect(cnt)
    cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    
    # 外接最小矩形
    min_rect = cv.minAreaRect(cnt)
    print(min_rect)
    
    box = cv.boxPoints(min_rect)
    box = np.int0(box)
    cv.drawContours(img, [box], 0, (0, 0, 255), 2)
    
    cv.imshow("draw", img)
    
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    boundingRect() 函数的返回值包含四个值,矩形框左上角的坐标 (x, y) 、宽度 w 和高度 h 。

    minAreaRect() 函数的返回值中还包含旋转信息,返回值信息为包括中心点坐标 (x,y) ,宽高 (w, h) 和旋转角度。

    轮廓近似

    根据我们指定的精度,它可以将轮廓形状近似为顶点数量较少的其他形状。它是由 Douglas-Peucker 算法实现的。

    OpenCV 提供的函数是 approxPolyDP(cnt, epsilon, True) ,第二个参数 epsilon 用于轮廓近似的精度,表示原始轮廓与其近似轮廓的最大距离,值越小,近似轮廓越拟合原轮廓。第三个参数指定近似轮廓是否是闭合的。具体用法如下:

    import cv2 as cv
    
    img = cv.imread("number.png")
    
    gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 降噪
    ret, thresh = cv.threshold(gray_img, 127, 255, 0)
    # 寻找轮廓
    contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    
    cnt = contours[0]
    
    # 计算 epsilon ,按照周长百分比进行计算,分别取周长 1% 和 10%
    epsilon_1 = 0.1 * cv.arcLength(cnt, True)
    epsilon_2 = 0.01 * cv.arcLength(cnt, True)
    
    # 进行多边形逼近
    approx_1 = cv.approxPolyDP(cnt, epsilon_1, True)
    approx_2 = cv.approxPolyDP(cnt, epsilon_2, True)
    
    # 画出多边形
    image_1 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
    image_2 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
    
    cv.polylines(image_1, [approx_1], True, (0, 0, 255), 2)
    cv.polylines(image_2, [approx_2], True, (0, 0, 255), 2)
    
    cv.imshow("image_1", image_1)
    cv.imshow("image_2", image_2)
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    第一张图是 epsilon 为原始轮廓周长的 10% 时的近似轮廓,第二张图中绿线就是 epsilon 为原始轮廓周长的 1% 时的近似轮廓。

    轮廓凸包

    凸包外观看起来与轮廓逼近相似,只不过它是物体最外层的「凸」多边形。

    如下图,红色的部分为手掌的凸包,双箭头部分表示凸缺陷(Convexity Defects),凸缺陷常用来进行手势识别等。

    import cv2 as cv
    
    img = cv.imread("number.png")
    gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 降噪
    ret, thresh = cv.threshold(gray_img, 127, 255, 0)
    # 寻找轮廓
    contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    cnt = contours[0]
    # 绘制轮廓
    image = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
    cv.drawContours(image, contours, -1, (0, 0 , 255), 2)
    
    # 寻找凸包,得到凸包的角点
    hull = cv.convexHull(cnt)
    
    # 绘制凸包
    cv.polylines(image, [hull], True, (0, 255, 0), 2)
    
    cv.imshow("image", image)
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    还有一个函数,是可以用来判断图形是否凸形的:

    print(cv.isContourConvex(hull)) # True
    

    它的返回值是 True 或者 False 。

    最小闭合圈

    接下来,使用函数 cv.minEnclosingCircle() 查找对象的圆周。它是一个以最小面积完全覆盖物体的圆。

    import cv2 as cv
    
    img = cv.imread("number.png")
    gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 降噪
    ret, thresh = cv.threshold(gray_img, 127, 255, 0)
    # 寻找轮廓
    contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    cnt = contours[0]
    
    # 绘制最小外接圆
    (x, y), radius = cv.minEnclosingCircle(cnt)
    center = (int(x), int(y))
    radius = int(radius)
    cv.circle(img, center, radius, (0, 255, 0), 2)
    
    cv.imshow("img", img)
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    下一个是把一个椭圆拟合到一个物体上。它返回内接椭圆的旋转矩形。

    ellipse = cv.fitEllipse(cnt)
    cv.ellipse(img, ellipse, (0, 255, 0), 2)
    

    参考

    https://zhuanlan.zhihu.com/p/61328775

    https://zhuanlan.zhihu.com/p/77783347

    您的扫码关注,是对小编坚持原创的最大鼓励:)
    展开全文
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...

    前文传送门:

    「Python 图像处理 OpenCV (1):入门」

    「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」

    「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」

    「Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间」

    「Python 图像处理 OpenCV (5):图像的几何变换」

    「Python 图像处理 OpenCV (6):图像的阈值处理」

    「Python 图像处理 OpenCV (7):图像平滑(滤波)处理」

    「Python 图像处理 OpenCV (8):图像腐蚀与图像膨胀」

    「Python 图像处理 OpenCV (9):图像处理形态学开运算、闭运算以及梯度运算」

    「Python 图像处理 OpenCV (10):图像处理形态学之顶帽运算与黑帽运算」

    「Python 图像处理 OpenCV (11):Canny 算子边缘检测技术」

    「Python 图像处理 OpenCV (12): Roberts 算子、 Prewitt 算子、 Sobel 算子和 Laplacian 算子边缘检测技术」

    「Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术」

    「Python 图像处理 OpenCV (14):图像金字塔」

    「Python 图像处理 OpenCV (15):图像轮廓」

    直方图

    首先,第一个问题是什么是直方图?

    直方图这个应该都知道吧,不知道的话就是下面这玩意:

    那么图像灰度直方图是什么鬼?

    直方图是都是由横纵坐标组成的,而图像直方图的横坐标 X 轴上表示的是像素值(不总是从 0 到 255 的范围),在纵坐标 Y 轴上表示的相应像素数。

    所以,直方图是可以对整幅图的灰度分布进行整体了解的图示,通过直方图我们可以对图像的对比度、亮度和灰度分布等有一个直观了解。

    还没看懂?简单地说,就是把一幅图像中每一个像素出现的次数都先统计出来,然后把每一个像素出现的次数除以总的像素个数,得到的就是这个像素出现的频率,然后再把像素与该像素出现的频率用图表示出来,就是灰度直方图。

    上面这张图来自官方网站,在这张图中,我们可以得到如下信息:

    • 左侧区域显示图像中较暗像素的数量(左侧的灰度级更趋近于 0 )。
    • 右侧区域则显示明亮像素的数量(右侧的灰度级更趋近于 255)。
    • 暗区域多于亮区域,而中间调的数量(中间值的像素值,例如127附近)则非常少。

    绘制直方图

    在绘制直方图的时候,有两种方法:

    1. 使用 Matplotlib 绘图功能。
    2. 使用 OpenCV 绘图功能。

    使用 Matplotlib 绘图

    Matplotlib 带有一个强大的直方图绘图功能:matplotlib.pyplot.hist() ,这个方法可以直接找到直方图进行绘制。

    在看示例代码之前,有两个参数需要先介绍下:

    • 数据源:数据源必须是一维数组,通常需要通过函数 ravel() 拉直图像,而函数 ravel() 的作用是将多维数组降为一维数组。
    • 像素级:一般是 256 ,表示 [0, 255] 。

    代码实现:

    import cv2 as cv
    import matplotlib.pyplot as plt
    
    img = cv.imread("maliao.jpg")
    
    cv.imshow("img", img)
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    plt.hist(img.ravel(), 256, [0, 256])
    plt.show()
    

    输出结果:

    当然,我们除了可以绘制灰度直方图以外,还可以绘制出 r,g,b 不同通道的直方图,可以看下面的代码:

    import cv2 as cv
    import matplotlib.pyplot as plt
    
    img = cv.imread("tiankong.jpg")
    color = ('b', 'g', 'r')
    
    cv.imshow("img", img)
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    for i, col in enumerate(color):
        histr = cv.calcHist([img], [i], None, [256], [0, 256])
        plt.plot(histr, color = col)
        plt.xlim([0, 256])
    plt.show()
    

    使用 OpenCV 绘制直方图

    使用 OpenCV 绘制直方图还是有点费劲儿的,首先我们确认横坐标是图像中各个像素点的灰度级,纵坐标是具有该灰度级的像素个数。

    接下来要介绍几个新概念:

    BINS:

    在前面的直方图中,我们显示的是每个像素值的像素数,即从 0 到 255 。那么现在会有一个问题,如果一个直方图我并不想找到所有的像素数量,而是取一定范围内的像素值,如:先找到 0 到 15 之间的像素数,然后找到 16 到 31 之间,…, 240 到 255 之间的像素数。

    这样,我们将这个直方图分成了 16 个子部分,每个子部分的值就是其中所有像素数的总和。每个子部分都称为 BIN 。在第一种情况下, BIN 的数量为 256 个(每个像素一个),而在第二种情况下, BIN 的数量仅为 16 个。

    DIMS:

    这是我们为其收集数据的参数的数量。在这种情况下,我们仅收集关于强度值的一件事的数据。所以这里是1。

    RANGE:

    这是要测量的强度值的范围。通常,它是 [0,256] ,即所有强度值。

    使用 OpenCV 的绘制直方图,我们会用到一个新的函数 calcHist() ,它的原函数如下:

    def calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None):
    
    • 参数1:要计算的原图,以方括号的传入,如:[img]。
    • 参数2:灰度图写[0]就行,彩色图 B/G/R 分别传入 [0]/[1]/[2] 。
    • 参数3:要计算的区域ROI,计算整幅图的话,写None。
    • 参数4:就是我们上面提到的 BINS ,子区段数目。
    • 参数5:range,要计算的像素值范围,一般为 [0,256] 。

    接下来我们开始画图,首先我们需要使用 calcHist() 来查找整个图像的直方图。

    import cv2 as cv
    import matplotlib.pyplot as plt
    
    img = cv.imread("tiankong.jpg")
    # 参数:原图像 通道[0]-B 掩码 BINS为256 像素范围0-255
    histB = cv.calcHist([img], [0], None, [256], [0, 255])
    histG = cv.calcHist([img], [1], None, [256], [0, 255])
    histR = cv.calcHist([img], [2], None, [256], [0, 255])
    
    cv.imshow("img", img)
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    plt.plot(histB, color='b')
    plt.plot(histG, color='g')
    plt.plot(histR, color='r')
    plt.show()
    

    直方图均衡化

    一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。

    • 灰度图均衡,直接使用 equalizeHist() 函数。
    • 彩色图均衡,分别在不同的通道均衡后合并。

    示例代码如下:

    import cv2 as cv
    import numpy as np
    
    img = cv.imread("dahai.jpg")
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    
    # 灰度图均衡化
    equ = cv.equalizeHist(gray)
    # 水平拼接原图和均衡图
    result1 = np.hstack((gray, equ))
    cv.imwrite('grey_equ.png', result1)
    
    # 彩色图像均衡化,需要分解通道 对每一个通道均衡化
    (b, g, r) = cv.split(img)
    bH = cv.equalizeHist(b)
    gH = cv.equalizeHist(g)
    rH = cv.equalizeHist(r)
    # 合并每一个通道
    equ2 = cv.merge((bH, gH, rH))
    # 水平拼接原图和均衡图
    result2 = np.hstack((img, equ2))
    cv.imwrite('bgr_equ.png', result2)
    

    结果:


    自适应直方图均衡

    上面介绍的直方图均值化是针对整幅图片的,这样有好处也有不好的地方,会导致一些图片部位太亮,导致大部分细节丢失。如下面这两张图片:

    直方图均衡后,背景对比度确实得到了改善。但是在两个图像中比较雕像的脸,由于亮度过高,丢失了大多数信息。

    因此,为了解决这个问题,引入了 自适应直方图均衡 来解决这个问题。

    它在每一个小区域内(默认 8×8 )进行直方图均衡化。当然,如果有噪点的话,噪点会被放大,需要对小区域内的对比度进行了限制。

    import cv2 as cv
    import numpy as np
    
    img = cv.imread('clahe_src.jpg', 0)
    
    # 全局直方图均衡
    equ = cv.equalizeHist(img)
    
    # 自适应直方图均衡
    clahe = cv.createCLAHE(clipLimit = 2.0, tileGridSize = (8, 8))
    cl1 = clahe.apply(img)
    
    # 水平拼接三张图像
    result1 = np.hstack((img, equ, cl1))
    
    cv.imwrite('clahe_result.jpg', result1)
    

    参考

    https://blog.csdn.net/Eastmount/article/details/83758402

    http://www.woshicver.com/FifthSection/4_10_1_%E7%9B%B4%E6%96%B9%E5%9B%BE-1%EF%BC%9A%E6%9F%A5%E6%89%BE%EF%BC%8C%E7%BB%98%E5%88%B6%EF%BC%8C%E5%88%86%E6%9E%90/

    https://zhuanlan.zhihu.com/p/61879400

    您的扫码关注,是对小编坚持原创的最大鼓励:)
    展开全文
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...
  • python 图像处理opencv

    2019-01-04 11:47:50
    可以用python 调用opencv, 调用方式: import cv2 主要函数: 图像的旋转 图像的旋转矩阵一般为:   但是单纯的这个矩阵是在原点处进行变换的,为了能够在任意位置进行旋转变换,opencv采用了另一种方式: ...
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 图像...
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」 「...
  • Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 图像属性 图像属性包括行数,列数和通道数,图像数据类型,像素数等。 1. 形状:shape ...
  • Python 图像处理 OpenCV (1):入门」 普通操作 1. 读取像素 读取像素可以通过行坐标和列坐标来进行访问,灰度图像直接返回灰度值,彩色图像则返回B、G、R三个分量。 需要注意的是, OpenCV 读取图像是 BGR 存储...
  • 又开一个新的系列分享,对图像处理感兴趣的同学可以关注这个系列。 更新频率尽量保持一周两到三次推送。 新系列第一件事儿当然是资源推荐,下面是一些有关 OpenCV 的资源链接: 资源链接: 官方网站:...
  • opencv-python安装 目前如果不指定版本,默认都是安装opencv 4.2以上版本的简化版,安装方式如下: pip install opencv-python # 如果上面的安装失败,请添加阿里或者清华源 pip install opencv-python -i ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,317
精华内容 2,926
关键字:

python图像处理opencv

python 订阅