一篇文章带你直观理解OpenCV:从操作到实战

在图像处理领域,OpenCV(Open Source Computer Vision Library)是一个广泛使用的计算机视觉库。它提供了丰富的函数接口,帮助我们处理图像和视频数据。本文将通过实例深入探讨OpenCV中常见的操作,帮助大家理解每个操作的核心原理,并通过代码示例加以实现。

目录

一、opencv常见操作

1.格式转换操作

1.1 灰度图转换

1.2 HSV转换

2. 二值化操作

3. 滤波操作

3.1 均值滤波

3.2 方框滤波

3.3 高斯滤波

3.4 中值滤波

4.形态学操作

4.1 腐蚀操作

4.2 膨胀操作

4.3 开运算(先腐蚀再膨胀)

4.4 闭运算(先膨胀再腐蚀)

4.5 梯度运算

4.6 礼帽(Top Hat)

4.7 黑帽(Black Hat)

5.边缘检测

5.1 Sobel算子

5.2 Scharr算子

5.3 Laplacian算子

5.4 Canny边缘检测

6.图像金字塔

6.1 高斯金字塔

6.2 拉普拉斯金字塔

7.图像轮廓

7.1 查找轮廓

7.2 绘制轮廓

7.3 轮廓特征

7.4 轮廓近似

7.5 外接矩形

7.6 外接圆

二、OpenCV 项目实战:文档扫描

1. 项目整体流程

2. 完整代码

3. 步骤解析

3.1 读取输入图像并设置参数

3.2 预处理:灰度化、模糊和边缘检测

3.3 轮廓检测

3.4 透视变换

3.5 二值化处理

3.6 保存结果


一、opencv常见操作

1.格式转换操作

1.1 灰度图转换

  • 通俗理解:将彩色图像转换为灰度图,常用于简化图像处理的复杂性,通常用于图像的预处理阶段。
  • 核心原理:从 BGR 颜色空间转换为灰度图,灰度图只保留亮度信息,去除色彩信息。
  • 代码示例
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
  • 参数说明
    • img: 输入图像,通常为彩色图像。
    • cv2.COLOR_BGR2GRAY: 指定颜色空间转换类型,将 BGR 图像转换为灰度图。

1.2 HSV转换

  • 通俗理解:将彩色图像转换为 HSV 颜色空间,HSV 更符合人眼感知的颜色模式,常用于颜色分割和特定颜色区域的检测。
  • 核心原理:通过色调、饱和度和亮度的三个分量来表示颜色,便于对颜色进行处理和分析。
  • 代码示例
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
  • 参数说明
    • img: 输入图像,通常为彩色图像。
    • cv2.COLOR_BGR2HSV: 指定颜色空间转换类型,将 BGR 图像转换为 HSV 图像。

2. 二值化操作

  • 通俗理解:将图像转换为黑白图像,通过设定一个阈值来决定哪些区域是白色,哪些是黑色,常用于图像的分割。
  • 核心原理:通过设定一个阈值,低于阈值的像素值变为黑色(0),高于阈值的像素值变为白色(255)。
  • 代码示例
    ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
    
  • 参数说明
    • img_gray: 输入的灰度图像。
    • 127: 阈值,低于该值的像素设为 0(黑),高于该值的像素设为 255(白)。
    • 255: 最大值,即像素值超过阈值时赋值为 255。
    • cv2.THRESH_BINARY_INV: 反向二值化,像素大于阈值设为 0,小于阈值设为 255。

3. 滤波操作

3.1 均值滤波

  • 通俗理解:用一个小窗口对图像进行模糊处理,降低图像中的噪声,产生柔和效果。
  • 核心原理:使用一个滑动窗口(如 3x3),对每个像素及其邻域像素进行平均,来平滑图像。
  • 代码示例
    blur = cv2.blur(img, (3, 3))
    
  • 参数说明
    • img: 输入图像。
    • (3, 3): 卷积核的大小,决定了模糊效果的程度。较大的卷积核会产生更强的模糊效果。

3.2 方框滤波

  • 通俗理解:类似于均值滤波,但可以通过设置 normalize 来控制是否归一化,常用于图像去噪。
  • 核心原理:通过滑动一个方形窗口,对每个窗口内的像素值求和并赋值给中心像素。可以控制是否归一化窗口内的像素总和。
  • 代码示例
    box = cv2.boxFilter(img, -1, (3, 3), normalize=True)
    
  • 参数说明
    • img: 输入图像。
    • -1: 输出图像深度与输入图像相同。
    • (3, 3): 卷积核大小,决定了处理区域的大小。
    • normalize=True: 是否归一化,True 会使得窗口内像素的总和为 1,False 则不归一化。

3.3 高斯滤波

  • 通俗理解:通过高斯函数进行模糊,能够更加平滑地去除噪声,常用于去噪和平滑图像。
  • 核心原理:使用一个高斯核(加权的卷积核)进行图像模糊,较中心的像素会有较大的权重,离中心较远的像素权重较小。
  • 代码示例
    gaussian = cv2.GaussianBlur(img, (5, 5), 1)
    
  • 参数说明
    • img: 输入图像。
    • (5, 5): 高斯核的大小,通常为奇数。较大的核会产生更强的模糊效果。
    • 1: 高斯函数的标准差,较大的标准差会产生更强的模糊效果。

3.4 中值滤波

  • 通俗理解:主要用于去除椒盐噪声,通过替换每个像素为邻域像素的中值来去噪。
  • 核心原理:对图像中的每个像素,取其邻域像素的中位数值来替换该像素值,有效去除椒盐噪声。
  • 代码示例
    median = cv2.medianBlur(img, 5)
    
  • 参数说明
    • img: 输入图像。
    • 5: 卷积核的大小,通常是奇数,决定了去噪的范围。

4.形态学操作

形态学操作(闭运算、梯度运算、礼帽和黑帽)都是基于形态学变换的,它们可以帮助在图像处理过程中提取和增强图像的细节,通常用于噪声去除、物体边界提取、图像分割等任务。

4.1 腐蚀操作

  • 通俗理解:腐蚀操作使得图像中的亮区域变小,暗区域变大,常用于去除小的噪点。
  • 核心原理:通过一个卷积核,将图像的每个像素与卷积核进行运算,只有当卷积核的全部区域都为“亮”时,中心像素才保持不变,其他情况下会变为“暗”。
  • 代码示例
    erosion = cv2.erode(img, kernel, iterations=1)
    
  • 参数说明
    • img: 输入图像。
    • kernel: 卷积核,控制腐蚀的形态。
    • iterations=1: 腐蚀的迭代次数,次数越多,腐蚀效果越明显。

4.2 膨胀操作

  • 通俗理解:膨胀操作会扩展图像中的亮区域,常用于填补腐蚀操作后留下的空洞。
  • 核心原理:通过卷积核,将图像的亮区域扩展,暗区域变小,能填补图像中的小孔洞。
  • 代码示例
    dilate = cv2.dilate(img, kernel, iterations=1)
    
  • 参数说明
    • img: 输入图像。
    • kernel: 卷积核,控制膨胀的形态。
    • iterations=1: 膨胀的迭代次数,次数越多,膨胀效果越明显。

4.3 开运算(先腐蚀再膨胀)

  • 通俗理解:开运算是先腐蚀后膨胀,通常用于去除图像中的小亮点或噪声。
  • 核心原理:开运算先腐蚀图像,去除小的亮点,再膨胀图像,恢复形状。
  • 代码示例
    opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
    
  • 参数说明
    • img: 输入图像。
    • kernel: 卷积核,控制操作的形态。
    • cv2.MORPH_OPEN: 选择开运算。如果换成 cv2.MORPH_CLOSE,则会执行闭运算(先膨胀后腐蚀)。

抱歉,遗漏了这部分内容。接下来我将按照你要求的格式,补充关于闭运算、梯度运算、礼帽和黑帽操作的详细介绍。


4.4 闭运算(先膨胀再腐蚀)

  • 通俗理解:闭运算通常用于填补图像中的小孔洞,它通过膨胀和腐蚀操作来去除噪声和空白区域。
  • 核心原理:闭运算是先膨胀图像,再进行腐蚀。膨胀操作先扩展亮区域,填补小孔洞,然后腐蚀操作恢复图像的结构,去除不需要的噪声。
  • 代码示例
    closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
    
  • 参数说明
    • img: 输入图像。
    • kernel: 卷积核,控制操作的形态。
    • cv2.MORPH_CLOSE: 选择闭运算。如果换成 cv2.MORPH_OPEN,则会执行开运算(先腐蚀后膨胀)。

4.5 梯度运算

  • 通俗理解:梯度运算用于提取图像中的边缘,通过计算膨胀和腐蚀的差异来突出显示图像的边界。
  • 核心原理:梯度运算计算图像膨胀和腐蚀结果的差异,强调图像的边缘信息。它可以突出显示物体轮廓。
  • 代码示例
    gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
    
  • 参数说明
    • img: 输入图像。
    • kernel: 卷积核,控制操作的形态。
    • cv2.MORPH_GRADIENT: 选择梯度运算,计算膨胀和腐蚀的差异。

4.6 礼帽(Top Hat)

  • 通俗理解:礼帽操作用于提取图像中的小亮点。它是通过图像和开运算结果的差异来提取亮点。
  • 核心原理:礼帽操作计算图像与开运算结果的差异,能够突出显示图像中的小亮点或细节,常用于图像中的小目标检测。
  • 代码示例
    tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
    
  • 参数说明
    • img: 输入图像。
    • kernel: 卷积核,控制操作的形态。
    • cv2.MORPH_TOPHAT: 选择礼帽操作,计算图像和开运算结果的差异。

4.7 黑帽(Black Hat)

  • 通俗理解:黑帽操作用于提取图像中的暗点。它是通过图像和闭运算结果的差异来提取暗点。
  • 核心原理:黑帽操作计算图像与闭运算结果的差异,能够突出显示图像中的暗点或细节,常用于背景分离和噪声去除。
  • 代码示例
    blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
    
  • 参数说明
    • img: 输入图像。
    • kernel: 卷积核,控制操作的形态。
    • cv2.MORPH_BLACKHAT: 选择黑帽操作,计算图像和闭运算结果的差异。

5.边缘检测

5.1 Sobel算子

  • 通俗理解:Sobel算子用于计算图像的梯度,可以帮助检测图像中的边缘信息。它通过对图像进行卷积操作,计算每个像素在水平方向(X轴)或垂直方向(Y轴)的变化。
  • 核心原理:Sobel算子通过分别计算X方向和Y方向的梯度(边缘的强度变化),然后结合这两个方向的信息来检测图像的边缘。常用的卷积核大小为3或5。
  • 代码示例
    sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
    sobelx = cv2.convertScaleAbs(sobelx)  # 转换为8位无符号整数图像
    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)  # 合并X和Y方向的梯度
    
  • 参数说明
    • img: 输入图像。
    • cv2.CV_64F: 输出图像的数据类型,64位浮点数。
    • 1, 00, 1: 分别表示在X轴或Y轴方向计算梯度。
    • ksize=3: 卷积核的大小,通常为3或5。

5.2 Scharr算子

  • 通俗理解:Scharr算子是比Sobel算子更精细的边缘检测算子,具有更高的精度。
  • 核心原理:Scharr算子与Sobel算子类似,但它通过更大的权重调整计算,以提高图像梯度计算的精度。它适用于需要更高精度边缘检测的情况。
  • 代码示例
    scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
    scharrx = cv2.convertScaleAbs(scharrx)
    scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
    scharry = cv2.convertScaleAbs(scharry)
    scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)
    
  • 参数说明
    • img: 输入图像。
    • cv2.CV_64F: 输出图像的数据类型,64位浮点数。
    • 1, 00, 1: 分别表示在X轴或Y轴方向计算梯度。

5.3 Laplacian算子

  • 通俗理解:Laplacian算子用于计算图像的二阶导数,通常用于检测图像的边缘,特别是对噪声敏感的情况。
  • 核心原理:拉普拉斯算子通过计算图像的二阶导数来检测边缘,能够突出显示图像中的强边缘和细节区域。它对图像的亮度变化比较敏感,可以有效检测图像中的边缘。
  • 代码示例
    laplacian = cv2.Laplacian(img, cv2.CV_64F)
    laplacian = cv2.convertScaleAbs(laplacian)
    
  • 参数说明
    • img: 输入图像。
    • cv2.CV_64F: 输出图像的数据类型,64位浮点数。

5.4 Canny边缘检测

  • 通俗理解:Canny边缘检测是一种多阶段的边缘检测算法,通过图像的梯度信息来检测边缘,能够精确地找出图像中的边缘。
  • 核心原理:Canny算法首先进行高斯平滑,减少噪声,然后计算梯度强度,并使用双阈值来确定边缘。它采用了边缘连接技术,能够提取清晰且连贯的边缘。
  • 代码示例
    v1 = cv2.Canny(img, 80, 150)
    
  • 参数说明
    • img: 输入图像。
    • 80150: 边缘检测的低阈值和高阈值。低阈值用于确定强边缘,高阈值用于连接弱边缘。

6.图像金字塔

6.1 高斯金字塔

  • 通俗理解:高斯金字塔用于图像的多尺度处理,通过不同的采样级别(上采样或下采样)来处理图像。
  • 核心原理:高斯金字塔是通过对图像进行逐层上采样或下采样,构建出图像的不同尺度,通常用于图像金字塔、特征提取等任务。
  • 代码示例
    up = cv2.pyrUp(img)  # 上采样
    down = cv2.pyrDown(img)  # 下采样
    
  • 参数说明
    • img: 输入图像。
    • cv2.pyrUp(): 上采样,放大图像。
    • cv2.pyrDown(): 下采样,缩小图像。

6.2 拉普拉斯金字塔

  • 通俗理解:拉普拉斯金字塔用于提取图像的多尺度细节信息,特别用于图像压缩、去噪等应用。
  • 核心原理:通过计算连续两层高斯金字塔图像之间的差异,得到每一层图像的细节部分。这种方法能够有效编码图像的多尺度信息。
  • 代码示例
    down = cv2.pyrDown(img)
    down_up = cv2.pyrUp(down)
    l_1 = img - down_up  # 图像与上采样后的结果相减,得到细节
    
  • 参数说明
    • img: 输入图像。
    • cv2.pyrDown(): 下采样。
    • cv2.pyrUp(): 上采样。

7.图像轮廓

7.1 查找轮廓

  • 通俗理解:查找图像中的轮廓是一种常用的边缘检测方法,它帮助提取图像中的形状和物体的边界。
  • 核心原理:通过图像的二值化或边缘检测结果,可以找到图像中所有的轮廓,并在图像中标记出来。
  • 代码示例
    contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
  • 参数说明
    • img: 输入图像。
    • cv2.RETR_EXTERNAL: 检索模式,仅查找外部轮廓。
    • cv2.CHAIN_APPROX_SIMPLE: 轮廓逼近方法,简化轮廓信息。

7.2 绘制轮廓

  • 通俗理解:绘制轮廓将提取出的轮廓显示到图像上,可以帮助分析图像的形状和结构。
  • 核心原理:通过将轮廓绘制到图像上,可以直观地查看物体的边界。
  • 代码示例
    draw_img = img.copy()
    res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
    
  • 参数说明
    • draw_img: 复制图像以便绘制。
    • contours: 轮廓数据。
    • -1: 绘制所有轮廓。
    • (0, 0, 255): 轮廓的颜色(红色)。
    • 2: 轮廓线宽度。

7.3 轮廓特征

  • 通俗理解:计算轮廓的特征(如面积、周长)有助于分析图像中的物体形状。
  • 核心原理:轮廓的面积和周长是描述物体形状的重要特征,可以帮助进行目标检测和形状分析。
  • 代码示例
    area = cv2.contourArea(cnt)  # 计算面积
    perimeter = cv2.arcLength(cnt, True)  # 计算周长
    
  • 参数说明
    • cnt: 输入轮廓。
    • cv2.contourArea(): 计算轮廓的面积。
    • cv2.arcLength:计算轮廓的周长,True`表示闭合轮廓。

7.4 轮廓近似

  • 通俗理解:轮廓近似用于简化轮廓,减少轮廓的点数,使得轮廓更加简洁。
  • 核心原理:通过近似轮廓,减少不必要的点,保留图像的基本形状。
  • 代码示例
    epsilon = 0.15 * cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, epsilon, True)
    
  • 参数说明
    • epsilon: 近似的精度,表示从原始轮廓到近似轮廓的最大距离。
    • cnt: 输入的轮廓。

7.5 外接矩形

  • 通俗理解:外接矩形用于获取图像中物体的边界框,常用于目标检测和物体定位。
  • 核心原理:通过计算轮廓的外接矩形,获得物体的最小边界框。
  • 代码示例
    x, y, w, h = cv2.boundingRect(cnt)
    img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    
  • 参数说明
    • cnt: 输入的轮廓。
    • cv2.boundingRect(): 获取轮廓的外接矩形。

7.6 外接圆

  • 通俗理解:外接圆用于获取图像中物体的最小圆形边界,常用于圆形物体检测。
  • 核心原理:通过计算轮廓的外接圆,得到物体的最小圆形边界。
  • 代码示例
    (x, y), radius = cv2.minEnclosingCircle(cnt)
    center = (int(x), int(y))
    radius = int(radius)
    img = cv2.circle(img, center, radius, (0, 255, 0), 2)
    
  • 参数说明
    • cnt: 输入的轮廓。
    • cv2.minEnclosingCircle(): 获取轮廓的外接圆。

二、OpenCV 项目实战:文档扫描

在这篇文章中,我们将通过一个完整的项目实战来巩固我们学习的 OpenCV 基本操作。本项目的目标是从一张扫描图像中提取出清晰的文档,并进行透视变换,最终输出经过处理的扫描图像。

1. 项目整体流程

首先,我们简要概括一下整个流程:

  1. 导入工具包
  2. 设置解析器对象
  3. 读取输入图像
  4. 图像预处理:灰度化、模糊和边缘检测
  5. 轮廓检测
  6. 遍历轮廓,查找矩形(4个点)
  7. 透视变换
  8. 二值化处理和保存结果

接下来,我们将逐步解析这些操作,并介绍其中涉及到的 OpenCV 常见操作。

2. 完整代码

import numpy as np
import argparse
import cv2

# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image to be scanned") 
args = vars(ap.parse_args())

def order_points(pts):
    # 一共4个坐标点
    rect = np.zeros((4, 2), dtype="float32")

    # 按顺序找到对应坐标0123分别是 左上,右上,右下,左下
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    # 计算右上和左下
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]

    return rect

def four_point_transform(image, pts):
    rect = order_points(pts)
    (tl, tr, br, bl) = rect

    # 计算输入的 w 和 h 值
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype="float32")

    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

    return warped

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    dim = None
    (h, w) = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r = height / float(h)
        dim = (int(w * r), height)
    else:
        r = width / float(w)
        dim = (width, int(h * r))
    resized = cv2.resize(image, dim, interpolation=inter)
    return resized

# 读取输入
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()

image = resize(orig, height=500)

# 预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)

# 展示预处理结果
print("STEP 1: 边缘检测")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]

# 遍历轮廓
for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)

    # 找到 4 个点的轮廓
    if len(approx) == 4:
        screenCnt = approx
        break

# 展示结果
print("STEP 2: 获取轮廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 透视变换
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)

# 二值处理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)

# 展示最终结果
print("STEP 3: 变换")
cv2.imshow("Original", resize(orig, height=650))
cv2.imshow("Scanned", resize(ref, height=650))
cv2.waitKey(0)

3. 步骤解析

3.1 读取输入图像并设置参数

在项目的开始部分,我们通过命令行解析器读取输入的图像路径,并且通过 resize() 函数调整图像的大小,以便更好地进行后续处理。

3.2 预处理:灰度化、模糊和边缘检测

我们通过 cv2.cvtColor() 将图像从 BGR 转为灰度图,接着使用 cv2.GaussianBlur() 对图像进行模糊处理,然后利用 cv2.Canny() 进行边缘检测。边缘检测的核心是通过梯度计算寻找图像中的显著变化点,也就是边缘。

在这部分,主要使用了以下 OpenCV 操作:

  • cv2.cvtColor():颜色空间转换,转换为灰度图。
  • cv2.GaussianBlur():高斯模糊,用于去噪。
  • cv2.Canny():Canny 边缘检测,用于提取图像边缘。

3.3 轮廓检测

在边缘图像的基础上,我们使用 cv2.findContours() 来查找图像中的轮廓,使用 cv2.contourArea() 对轮廓进行排序,取面积最大的前五个轮廓。接着,我们使用 cv2.approxPolyDP() 对每个轮廓进行多边形逼近,最终找出包含四个点的矩形轮廓。

涉及的 OpenCV 操作:

  • cv2.findContours():查找轮廓。
  • cv2.contourArea():计算轮廓的面积。
  • cv2.approxPolyDP():轮廓多边形逼近。

3.4 透视变换

当我们找到文档的四个边缘点后,我们通过 cv2.getPerspectiveTransform() 计算透视变换矩阵,并使用 cv2.warpPerspective() 将原图进行透视变换,得到一个矩形化的图像。

涉及的 OpenCV 操作:

  • cv2.getPerspectiveTransform():获取透视变换矩阵。
  • cv2.warpPerspective():执行透视变换。

3.5 二值化处理

为了进一步增强图像的对比度,我们使用 cv2.threshold() 对透视变换后的图像进行二值化处理,将图像转为黑白图像。

涉及的 OpenCV 操作:

  • cv2.threshold():图像二值化。

3.6 保存结果

最终,我们将二值化后的图像保存为文件,并通过 cv2.imshow() 展示原图与扫描图像的对比效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值