Python基于OpenCV实现文档扫描与图像风格迁移

 更新时间:2026年08月31日 09:41:27   作者:陈年老古董  
想知道怎么用OpenCV把摄像头画面中的文档自动摆正并二值化吗,这篇文章手把手教你边缘检测、轮廓近似和透视变换的完整流程,还附赠一个用dnn模块实现风格迁移的小例子,把普通照片变成卡通效果,代码不长但很实用,快来试试吧

最近又捣鼓了两个 OpenCV 的小项目,一个是实时检测摄像头画面中的文档,通过透视变换把它“摆正”并二值化,有点像手机扫描软件;另一个是用训练好的神经网络模型对图片做风格迁移,把普通照片变成卡通或油画效果。代码都不长,但涉及的知识点挺实用,整理出来分享一下。

一、文档扫描与透视变换

这个例子实现了一个实时文档扫描功能:打开摄像头,检测画面中的四边形文档,然后利用透视变换把倾斜的文档转成正视图,最后做二值化处理,方便后续识别或保存。核心步骤是:边缘检测 → 轮廓近似 → 找到四边形 → 透视变换。

1. 导入库和辅助函数

import numpy as np
import cv2
def cv_show(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(1)
  • 导入 numpy 和 cv2。
  • 定义 cv_show 函数,用于快速显示图像,waitKey(1) 表示显示 1 毫秒,适合在循环中实时刷新。

2. 坐标排序函数 order_points

def order_points(pts):
    # 一共4个坐标点
    rect = np.zeros((4, 2), dtype="float32")  # 用来存储排序之后的坐标位置
    # 按顺序找到对应坐标0123分别是 左上,右上,右下,左下
    s = pts.sum(axis=1)  # 对pts矩阵的每一行进行求和操作。(x+y)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    diff = np.diff(pts, axis=1)  # 对pts矩阵的每一行进行求差操作。(y-x)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

这个函数的作用是把随意输入的四个点按“左上、右上、右下、左下”的顺序排好。

  • s = pts.sum(axis=1):计算每个点的 x+y,和最小的点就是左上角(因为 x 和 y 都小),和最大的点就是右下角。
  • diff = np.diff(pts, axis=1):计算每个点的 y‑x,差最小的点(即 y 小 x 大)是右上角,差最大的点(y 大 x 小)是左下角。
  • 这样排序后,后面做透视变换时四个点就能和标准矩形的四个角一一对应。

3. 透视变换函数 four_point_transform

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, dsize=(maxWidth, maxHeight))

    # 返回变换后结果
    return warped
  • 计算文档实际宽度:取底边长度和顶边长度的最大值,因为透视可能导致两边不一样长。
  • 计算实际高度:取左边长度和右边长度的最大值。
  • 定义目标矩形的四个角点,左上角为原点,宽为 maxWidth,高为 maxHeight
  • cv2.getPerspectiveTransform 计算变换矩阵,cv2.warpPerspective 执行透视变换,把原图中的四边形区域拉伸成矩形。

4. 打开摄像头并循环读取

cap = cv2.VideoCapture(0)  # 确保摄像头是可以启动的状态。
if not cap.isOpened():  # 打开失败
    print("Cannot open camera")
    exit()

while True:
    flag = 0  # 用于标识 当前是否检测到文档
    ret, image = cap.read()  # 如果正确读取帧,ret为True
    image=cv2.flip(image, 1) #画面反转
    orig = image.copy()
    if not ret:  # 读取失败,则退出循环
        print("不能读取摄像头")
        break
    cv_show("image", image)
  • 打开默认摄像头(索引 0)。
  • 循环读取每一帧,cv2.flip(image, 1) 水平翻转,让画面像照镜子一样,更自然。
  • orig 保存一份原始图像的副本,后面做透视变换用原图。
  • flag 用来标记当前帧是否检测到文档,初始为 0。

5. 预处理:灰度、高斯模糊、边缘检测

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, ksize=(5, 5), sigmaX=0)
    edged = cv2.Canny(gray, threshold1=15, threshold2=45)
    cv_show('1', edged)
  • 转灰度图,减少计算量。
  • 高斯模糊去噪,让边缘更平滑。
  • Canny 边缘检测,两个阈值 15 和 45,低于 15 的不是边缘,高于 45 的肯定是边缘,之间的看连通性。这里阈值较低,能检测到较弱的边缘。

6. 查找轮廓并排序

    cnts = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:3]
    image_contours = cv2.drawContours(image, cnts, -1, color=(0, 255, 0), thickness=2)
    cv_show("image_contours", image_contours)
  • 查找外部轮廓。
  • 按轮廓面积从大到小排序,取前三个最大的轮廓(因为文档通常是画面中最大的四边形)。
  • 把前三个轮廓画出来,方便调试观察。

7. 遍历轮廓,找到四边形文档

    for c in cnts:
        # 计算轮廓近似
        peri = cv2.arcLength(c, closed=True)  #计算轮廓的周长
        # C表示输入的点集
        # epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确度参数
        # True表示封闭的
        approx = cv2.approxPolyDP(c, 0.05 * peri, closed=True)  # 轮廓近似
        area = cv2.contourArea(approx)
        # 4个点的时候就拿出来
        if area > 30000 and len(approx) == 4:  #20000
            screenCnt = approx
            flag = 1
            print(peri, area)
            print('检测到文档')
            break
  • 计算轮廓周长 peri
  • cv2.approxPolyDP 对轮廓进行多边形逼近,0.05 * peri 表示近似精度,值越小越接近原轮廓,值越大越简化。这里希望把轮廓近似成四边形。
  • 如果近似后的多边形有 4 个顶点,并且面积大于 30000(过滤小物体),就认为找到了文档,记录下四个顶点,设置 flag=1 并跳出循环。

8. 如果检测到文档,进行透视变换和二值化

    if flag == 1:
        # 展示结果
        # print("STEP 2: 获取轮廓")
        image_contours = cv2.drawContours(image, contours=[screenCnt], contourIdx=0, color=(0, 255, 0), thickness=2)
        cv_show("image", image_contours)
        # 透视变换
        warped = four_point_transform(orig, screenCnt.reshape(4, 2))
        cv_show("warped", warped)
        # 二值处理
        warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
        ref = cv2.threshold(warped,135, 255, cv2.THRESH_BINARY)[1]
        # ref = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        cv_show("ref", ref)
    if cv2.waitKey(20)==27:
        break
  • 在原图上画出找到的四边形。
  • 调用 four_point_transform,传入原始图像和四个顶点(reshape(4,2) 把形状从 (4,1,2) 变成 (4,2)),得到矫正后的文档图像。
  • 转灰度,然后二值化:阈值 135,大于 135 变为 255(白色),小于变为 0(黑色)。这样文档就变成黑白分明,类似扫描件。

9. 释放资源

cap.release()
cv2.destroyAllWindows()
  • 释放摄像头和关闭所有窗口。

小结:这个例子完整演示了从摄像头采集图像、边缘检测、轮廓逼近到透视变换的流程,是实现文档扫描的基础。实际应用中还可以加入自动检测阈值、去除阴影等优化。

二、图像风格迁移:用神经网络把照片变成卡通画

第二个例子使用一个已经训练好的风格迁移模型(candy.t7)对输入图片进行处理,生成卡通风格的图像。OpenCV 的 dnn 模块可以加载多种深度学习模型,使用起来很方便。

1. 读取并显示输入图像

import cv2
image = cv2.imread('haimianbaobao.png')
cv2.imshow('yuan tu', image)
cv2.waitKey(0)
  • 读取一张图片(这里是海绵宝宝),显示原图。按任意键继续。

2. 图像预处理 blobFromImage

(h, w) = image.shape[:2]
blob = cv2.dnn.blobFromImage(image, scalefactor=1, size=(w, h), mean=(0, 0, 0), swapRB=False, crop=False)
  • 深度学习模型通常要求输入固定尺寸的“blob”(四维数组:batch, channels, height, width)。
  • cv2.dnn.blobFromImage 的作用是把 OpenCV 图像转换成 blob。主要参数:
    • scalefactor=1:不缩放像素值。
    • size=(w, h):这里直接用了原图尺寸,但很多模型要求固定尺寸(比如 224×224),使用时要看模型说明。此处因为模型是 torch 的,可能对任意尺寸兼容。
    • mean=(0,0,0):不减去均值。
    • swapRB=False:不交换 BGR 到 RGB,因为模型训练时可能用的就是 BGR。
  • 最终得到一个 blob,形状是 (1, 3, h, w)

3. 加载模型并前向传播

net = cv2.dnn.readNet(r'model\candy.t7')
net.setInput(blob)
out = net.forward()
  • cv2.dnn.readNet 加载模型文件。candy.t7 是一个 Torch 格式的模型,能把图片转换为糖果/卡通风格。
  • setInput 设置输入 blob。
  • forward 执行前向传播,输出结果。输出也是一个四维数组,形状通常是 (1, C, H, W),C 是通道数(彩色 图一般为 3)。

4. 输出后处理并显示

# ======输出处理=======
# 重塑形状(忽略第1维),4维变3维
# 调整输出out的形状,模型推理输出out是四维BCHW形式的,调整为三维CHW形式
out_new = out.reshape(out.shape[1], out.shape[2], out.shape[3])

# 对输入的数组(或图像)进行归一化处理,使其数值范围在指定的范围内
cv2.normalize(out_new, out_new, norm_type=cv2.NORM_MINMAX)

# 转置输出结果的维度
result = out_new.transpose(1, 2, 0)

# 显示转换后的图像
cv2.imshow('Stylized Image', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 输出 out 是四维 (1, C, H, W),用 reshape 去掉第一维 batch,变成 (C, H, W)
  • cv2.normalize 把像素值范围缩放到 01 或 0255(取决于数据类型),这里使用 NORM_MINMAX 线性归一化。
  • 转置维度从 (C, H, W) 变成 (H, W, C),这是 OpenCV 显示图像需要的格式(高、宽、通道)。
  • 显示风格化后的图片。

小结:这个例子展示了 OpenCV 的 dnn 模块如何加载预训练模型并做推理,核心就三步:blobFromImage 预处理、forward 前向传播、后处理转回图像。更换不同的模型文件就能实现不同的风格效果。

以上两个例子覆盖了 OpenCV 中传统图像处理和深度学习模块的常见用法,代码都不复杂,跑通之后可以再根据自己的需求调整参数或模型。希望对你有所帮助,有问题欢迎在评论区留言。

以上就是Python基于OpenCV实现文档扫描与图像风格迁移的详细内容,更多关于Python OpenCV文档扫描与图像风格迁移的资料请关注脚本之家其它相关文章!

相关文章

  • Python提取PDF指定内容并生成新文件

    Python提取PDF指定内容并生成新文件

    本文为你展示,如何用Python把许多PDF文件的文本内容批量提取出来,并且整理存储到数据框中,以便于后续的数据分析。感兴趣的可以了解一下
    2021-06-06
  • Python使用Flask后端实现解析Excel图表(附源码)

    Python使用Flask后端实现解析Excel图表(附源码)

    这篇文章主要介绍了一个完整的 Flask + Vue 3 前端模板 方案,实现 上传 Excel 文件(不再用链接),后端解析 chart1.xml,返回结构化数据,前端用 ECharts 渲染图表,感兴趣的小伙伴可以了解下
    2026-01-01
  • 解决pytorch找不到shm.dll模块的问题

    解决pytorch找不到shm.dll模块的问题

    本文主要介绍了解决pytorch找不到shm.dll模块的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-04-04
  • 如何使用pytorch构建高斯混合模型分类器

    如何使用pytorch构建高斯混合模型分类器

    本文是一个利用Pytorch构建高斯混合模型分类器的尝试,我们将从头开始构建高斯混合模型(GMM),这样可以对高斯混合模型有一个最基本的理解,本文不会涉及数学,需要的朋友可以参考下
    2023-10-10
  • 从零开始理解如何使用Python搭建智能AI代理

    从零开始理解如何使用Python搭建智能AI代理

    Agentic AI(智能代理)正在悄然改变我们的工作方式,所以这篇文章小编就来和大家简单介绍一下如何使用Python搭建智能AI代理,感兴趣的小伙伴可以了解下
    2025-07-07
  • Python中使用Boolean操作符做真值测试实例

    Python中使用Boolean操作符做真值测试实例

    这篇文章主要介绍了Python中使用Boolean操作符做真值测试实例,在Python中,任何类型的对象都可以做真值测试,并且保证返回True或者False,需要的朋友可以参考下
    2015-01-01
  • python自动化测试之从命令行运行测试用例with verbosity

    python自动化测试之从命令行运行测试用例with verbosity

    这篇文章主要介绍了python自动化测试之从命令行运行测试用例with verbosity,是一个较为经典的自动化测试实例,需要的朋友可以参考下
    2014-09-09
  • 跟老齐学Python之玩转字符串(1)

    跟老齐学Python之玩转字符串(1)

    本文主要讲诉了字符串的定义,变量链接到字符串,以及对字符串的简单操作,都是些非常基础的东西,适合零基础的Pythoner学习,觉得有用的话,多鼓鼓掌吧
    2014-09-09
  • Python openpyxl模块学习之轻松玩转Excel

    Python openpyxl模块学习之轻松玩转Excel

    Python提供了许多操作Excel的模块,能够让我们从繁琐的工作中腾出双手。本文主要为大家介绍的是openpyxl模块,它的功能相对与其他模块更为齐全,感兴趣的小伙伴快来学习一下吧
    2021-12-12
  • Python二次规划和线性规划使用实例

    Python二次规划和线性规划使用实例

    这篇文章主要介绍了Python二次规划和线性规划使用实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12

最新评论