当前位置: 代码网 > it编程>前端脚本>Python > Python基于OpenCV实现文档扫描与图像风格迁移

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

2026年08月31日 Python 我要评论
最近又捣鼓了两个 opencv 的小项目,一个是实时检测摄像头画面中的文档,通过透视变换把它“摆正”并二值化,有点像手机扫描软件;另一个是用训练好的神经网络模型对图片做风格迁移

最近又捣鼓了两个 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文档扫描与图像风格迁移的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com