opencv⚠️实战⚠️女子深夜久久不能入眠,300行写出全能扫描王!☢️建议手收藏☢️(代码片段)

我是小白呀 我是小白呀     2022-12-14     295

关键词:

【OpenCV】 ⚠️实战⚠️ 女子深夜久久不能入眠,300行写出全能扫描王! ☢️建议手收藏☢️

概述

今天带大家使用我们之前学会的知识来实现一个简易版的全能扫描王. 代码分为 3 个部分: 主函程序, 预处理, 其他程序.

图像透视

透视变换 (Perspective Transformation) 是将成像投影到一个新的视平面 (Viewing Plane). 通过改变图像或视屏的透视图, 我们可以更好的了解所需信息. 如图:

获取透视矩阵

格式:

M = cv2.getPerspectiveTransform(src, dst, solveMethod=None)

参数:
src: 源图像中四边形顶点的坐标
dst: 目标图像中相应四边形顶点的坐标

返回值:

  • M: 透视变换矩阵

矩阵例子:

[[ 8.75046884e+00 -9.23660638e+00  0.00000000e+00]
 [ 4.43412734e-01 -6.70045886e+00  2.19700000e+03]
 [ 2.51096074e-03 -3.35871928e-03  1.00000000e+00]]

透视变换

格式:

cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)

参数:

  • src: 输入图片
  • M: 透视变换矩阵
  • dsize: 输出图片的大小

返回值:

  • 透视变换后的图片

例子:

预处理

通过图片伸缩, 灰度转换, 高斯滤波, 边缘检测等方法, 实现图片预处理

代码:

import cv2
from matplotlib import pyplot as plt
from my_functions import resize
from my_functions import four_point_transform


def read_image(image_path, visualize=False):
    """
    读取图片
    :param image_path: 图片路径
    :param visualize: 可视化, 默认为False
    :return: 返回原始图片, 裁剪后的图片, 边缘, 图片伸缩比例
    """

    # 读取图片
    image = cv2.imread(image_path)

    # 计算伸缩比例
    ratio = image.shape[0] / 500.0

    # 深拷贝
    image_copy = image.copy()

    # 缩放大小
    image_resize = resize(image_copy, height=500)

    # 转换成灰度图
    image_gray = cv2.cvtColor(image_resize, cv2.COLOR_BGR2GRAY)

    # 高斯滤波
    image_gaussian = cv2.GaussianBlur(image_gray, (5, 5), 0)

    # Canny边缘检测
    edge = cv2.Canny(image_gaussian, 75, 200)

    if visualize:
        """图片展示"""

        # 绘制子图
        f, ax = plt.subplots(2, 2, figsize=(8, 10))
        ax[0, 0].imshow(cv2.cvtColor(image_resize, cv2.COLOR_BGR2RGB))
        ax[0, 1].imshow(image_gray, "gray")
        ax[1, 0].imshow(image_gaussian, "gray")
        ax[1, 1].imshow(edge, "gray")

        # 去除x, y坐标
        ax[0, 0].set_xticks([])
        ax[0, 0].set_yticks([])
        ax[0, 1].set_xticks([])
        ax[0, 1].set_yticks([])
        ax[1, 0].set_xticks([])
        ax[1, 0].set_yticks([])
        ax[1, 1].set_xticks([])
        ax[1, 1].set_yticks([])

        # 标题
        ax[0, 0].set_title("original")
        ax[0, 1].set_title("image gray")
        ax[1, 0].set_title("image gaussian blur")
        ax[1, 1].set_title("image edge")

        plt.show()

    # 返回
    return image, image_resize, edge, ratio


def image_calculate_contours(image_resize, edge, visualize=False):
    """
    计算轮廓
    :param image_resize: 裁剪后的图片
    :param edge: 边缘
    :param visualize: 可视化, 默认为False
    :return: 外接长方形数组
    """

    # 轮廓检测
    contours, hierarchy = cv2.findContours(edge.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    # 轮廓排序
    contours = sorted(contours, key=cv2.contourArea, reverse=True)

    # 取最大的5个
    contours_5 = contours[:5]

    # 长方形数字
    approx_array = []

    # 遍历轮廓
    for c in contours_5:

        # 计算轮廓周长
        length = cv2.arcLength(c, True)

        # 计算大约轮廓
        approx = cv2.approxPolyDP(c, 0.02 * length, True)

        # 如果是四边形, 取出
        if len(approx) == 4:
            approx_array.append(approx)

    if visualize:
        """图片展示"""

        # 绘制轮廓1
        draw_img_1 = cv2.drawContours(image_resize.copy(), contours, 0, (0, 255, 255), 2)

        # 绘制轮廓2
        draw_img_2 = cv2.drawContours(image_resize.copy(), contours_5, 0, (0, 255, 255), 2)

        # 绘制轮廓3
        draw_img_3 = cv2.drawContours(image_resize.copy(), approx_array, 0, (0, 255, 255), 2)

        # 绘制子图
        f, ax = plt.subplots(2, 2, figsize=(8, 10))
        ax[0, 0].imshow(cv2.cvtColor(image_resize, cv2.COLOR_BGR2RGB))
        ax[0, 1].imshow(cv2.cvtColor(draw_img_1, cv2.COLOR_BGR2RGB))
        ax[1, 0].imshow(cv2.cvtColor(draw_img_2, cv2.COLOR_BGR2RGB))
        ax[1, 1].imshow(cv2.cvtColor(draw_img_3, cv2.COLOR_BGR2RGB))

        # 去除x, y坐标
        ax[0, 0].set_xticks([])
        ax[0, 0].set_yticks([])
        ax[0, 1].set_xticks([])
        ax[0, 1].set_yticks([])
        ax[1, 0].set_xticks([])
        ax[1, 0].set_yticks([])
        ax[1, 1].set_xticks([])
        ax[1, 1].set_yticks([])

        # 标题
        ax[0, 0].set_title("original")
        ax[0, 1].set_title("contours")
        ax[1, 0].set_title("contours biggest 5")
        ax[1, 1].set_title("contours approx")

        plt.show()

    # 返回
    return approx_array


def image_transform(image, approx_array, ratio, visualize=False):
    """
    图片转换
    :param image: 原始图像
    :param approx_array: 外接长方形坐标数组
    :param ratio: 图片拉伸比例
    :param visualize: 可视化, 默认为False
    :return: 返回最终结果
    """

    # 透视变换
    warped = four_point_transform(image, approx_array[0].reshape(4, 2) * ratio)

    # 转换为灰度图
    warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)

    # 二值化
    ret, thresh = cv2.threshold(warped_gray, 100, 255, cv2.THRESH_BINARY)

    if visualize:
        """图片展示"""

        # 绘制子图
        f, ax = plt.subplots(2, 2, figsize=(8, 10))
        ax[0, 0].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
        ax[0, 1].imshow(cv2.cvtColor(warped, cv2.COLOR_BGR2RGB))
        ax[1, 0].imshow(warped_gray, "gray")
        ax[1, 1].imshow(thresh, "gray")

        # 去除x, y坐标
        ax[0, 0].set_xticks([])
        ax[0, 0].set_yticks([])
        ax[0, 1].set_xticks([])
        ax[0, 1].set_yticks([])
        ax[1, 0].set_xticks([])
        ax[1, 0].set_yticks([])
        ax[1, 1].set_xticks([])
        ax[1, 1].set_yticks([])

        # 标题
        ax[0, 0].set_title("original")
        ax[0, 1].set_title("warped")
        ax[1, 0].set_title("warped gray")
        ax[1, 1].set_title("thresh")

        plt.show()

    # 返回
    return warped_gray

其他函数

import numpy as np
import cv2


def order_points(points):
    """
    坐标点排序
    :param points: 轮廓坐标
    :return: 返回排序完的坐标
    """

    # 一共4个坐标点, 左上, 右上, 右下, 左下
    rect = np.zeros((4, 2), dtype=np.float32)

    # 计算左上, 右下
    s = points.sum(axis=1)
    rect[0] = points[np.argmin(s)]  # 和最小的是左上
    rect[2] = points[np.argmax(s)]  # 和最大的是右下

    # 计算右上, 左下
    diff = np.diff(points, axis=1)
    rect[1] = points[np.argmin(diff)]  # 差最小的是右上
    rect[3] = points[np.argmax(diff)]  # 差最小的是左下

    # 返回
    return rect


def four_point_transform(image, pts):
    """
    透视变换 (拉伸为长方形)
    :param image: 原始图像
    :param pts: 坐标点
    :return: 透视变换后的图
    """

    # 获取输入坐标点
    rect = order_points(pts)
    (top_left, top_right, bottom_left, bottom_right) = rect

    # 计算最大的w (勾股定理w = (Δx^2 + Δy^2)^1/2)
    widthA = np.sqrt(((bottom_right[0] - bottom_left[0]) ** 2) + ((bottom_right[1] - bottom_left[1]) ** 2))
    widthB = np.sqrt(((top_right[0] - top_left[0]) ** 2) + ((top_right[1] - top_left[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    # 计算最大的y (勾股定理y = (Δx^2 + Δy^2)^1/2)
    heightA = np.sqrt(((top_right[0] - bottom_right[0]) ** 2) + ((top_right[1] - bottom_right[1]) ** 2))
    heightB = np.sqrt(((top_left[0] - bottom_left[0]) ** 2) + ((top_right[1] - top_left[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    # 变换后对应坐标位置
    dst = np.array(
        [[0, 0],
         [maxWidth - 1, 0],
         [maxWidth - 1, maxHeight - 1],
         [0, maxHeight - 1]],
        dtype=np.float32
    )

    # 计算变换矩阵
    M = cv2.getPerspectiveTransform(rect, dst)
    print("变换矩阵:\\n", M)  # 调试输出

    # 透视变换
    wraped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

    # 返回
    return wraped


def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    """
    修改图片大小
    :param image: 原图
    :param width: 宽
    :param height: 高
    :param inter: 模式
    :return: 修改好的图片
    """

    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

主函数

import argparse
import cv2
from pre_process import read_image
from pre_process import image_calculate_contours
from pre_process import image_transform


def parse_opt():
    """设置参数"""

    parser = argparse.ArgumentParser()
    parser.add_argument("--image_path", type=str, default="images/receipt2.jpg", help="图片路径")
    args = parser.parse_args()

    return args


def main():
    """主函数"""

    args = parse_opt()

    # 读取图片
    image, image_resize, edge, ratio = read_image(image_path=args.image_path, visualize=True)

    # 计算轮廓
    approx_array = image_calculate_contours(image_resize, edge, visualize=True)

    # 图片转换
    final_result = image_transform(image=image, approx_array=approx_array, ratio=ratio, visualize=True)

    # 保存最终结果
    cv2.imwrite("final_result.jpg", final_result)

if __name__ == "__main__":
    main()

输出结果



最终转换结果

原图:


最终结果:

opencv⚠️实战⚠️人脸识别☢️建议手收藏☢️(代码片段)

【OpenCV】⚠️实战⚠️人脸识别☢️建议手收藏☢️概述模型获取detectMultiScale图片人脸识别视频人脸识别概述OpenCV是一个跨平台的计算机视觉库,支持多语言,功能强大.今天小白就带大家来实战一下,用OpenCV实现人脸识别.模型获取... 查看详情

opencv-python实战——opencv中绘制图形与文本(万字总结,️❤️建议收藏️❤️)(代码片段)

OpenCV-Python实战(3)——OpenCV中绘制图形与文本(万字总结,️📕建议收藏📕)0.前言1.OpenCV绘图基础2.OpenCV绘制图形2.1基本图形的绘制2.1.1直线2.1.2矩形2.1.3圆形2.2高级图形的绘制2.2.1剪裁线2.2.2箭头2.2.3... 查看详情

opencv⚠️实战⚠️银行卡卡号读取(代码片段)

【OpenCV】⚠️实战⚠️银行卡卡号读取概述预处理代码模板预处理银行卡预处理计算轮廓代码模板轮廓银行卡轮廓其他程序主函数代码数字分割最终结果概述今天带大家使用我们之前学会的知识来实现银行卡卡号读取.代码分为... 查看详情

opencv-python实战——opencv图像运算(❤️万字长文,含大量示例❤️)(代码片段)

OpenCV-Python实战(5)——OpenCV图像运算(❤️万字长文,含大量示例❤️)0.前言1.饱和运算2.图像加减法与图像混合3.按位运算4.形态变换4.1膨胀运算与腐蚀运算4.2开运算与闭运算4.3形态梯度运算4.4顶帽运算与... 查看详情

opencv-python实战——opencv常见图像处理技术(❤️万字长文,含大量示例❤️)(代码片段)

OpenCV-Python实战(4)——OpenCV常见图像处理技术(❤️万字长文,含大量示例❤️)0.前言1.拆分与合并通道2.图像的几何变换2.1缩放图像2.2平移图像2.3旋转图像2.4图像的仿射变换2.5图像的透视变换2.6裁剪图像3.... 查看详情

opencv-python实战——opencv中的色彩空间和色彩映射(❤️含大量实例,建议收藏❤️)(代码片段)

OpenCV-Python实战(6)——OpenCV中的色彩空间和色彩映射(❤️含大量示例❤️,建议收藏)0.前言1.色彩空间1.1显示色彩空间1.2不同色彩空间在皮肤分割中的不同效果2.色彩映射2.1OpenCV中的色彩映射2.2自定义色彩... 查看详情

opencv-python实战——opencv简介与图像处理基础(万字总结,️建议收藏️)(代码片段)

OpenCV-Python实战(1)——OpenCV简介与图像处理基础(内含大量示例,📕建议收藏📕)OpenCV介绍Python安装OpenCVOpenCV主要模块OpenCV应用场景OpenCV图像处理基础图像基础图像处理中的主要问题图像处理流程像... 查看详情

opencv-python实战——图像与视频文件的处理(两万字详解,️建议收藏️)(代码片段)

OpenCV-Python实战(2)——图像与视频文件的处理(两万字详解,️📕建议收藏📕)0.前言1.图像与视频文件处理基础2.图像的读取与写入2.1在OpenCV中读取图像2.2使用OpenCV写入图像2.3计算机视觉项目处理流... 查看详情

opencv-python实战——直方图详解(❤️含大量实例,建议收藏❤️)(代码片段)

OpenCV-Python实战(7)——直方图详解(❤️含大量示例,建议收藏❤️)0.前言1.直方图简介1.1直方图相关术语2.灰度直方图2.1不带蒙版的灰度直方图2.2带有蒙版的灰度直方图3.颜色直方图4.直方图的自定义可视... 查看详情

opencv⚠️高手勿入!半小时学会基本操作13⚠️直线检测(代码片段)

【OpenCV】⚠️高手勿入!半小时学会基本操作13⚠️直线检测概述霍夫直线变换原理详解代码实战HoughLinesHoughLinesP概述OpenCV是一个跨平台的计算机视觉库,支持多语言,功能强大.今天小白就带大家一起携手走进OpenCV的世界.(第13课)霍... 查看详情

opencv教程之4.1.0版与visualstudio2015环境搭建☀️《❤️记得收藏❤️》(代码片段)

OpenCV教程之4.1.0版与VisualStudio2015环境搭建☀️《❤️记得收藏❤️》目录🏳️‍🌈开讲啦!!!!🏳️‍🌈🗾1、下载所需软件🏔️2、安装软件⛰️3、新建工程🌋4、工程环境配置&#x... 查看详情

opencv⚠️高手勿入!半小时学会基本操作24⚠️sift算法(代码片段)

【OpenCV】⚠️高手勿入!半小时学会基本操作24⚠️SIFT算法概述图像尺度空间多分辨率金字塔高斯差分金字塔计算极值点SIFT算法函数实战概述OpenCV是一个跨平台的计算机视觉库,支持多语言,功能强大.今天小白就带大家一起携手走... 查看详情

opencv⚠️高手勿入!半小时学会基本操作24⚠️sift算法(代码片段)

【OpenCV】⚠️高手勿入!半小时学会基本操作24⚠️SIFT算法概述图像尺度空间多分辨率金字塔高斯差分金字塔计算极值点SIFT算法函数实战概述OpenCV是一个跨平台的计算机视觉库,支持多语言,功能强大.今天小白就带大家一起携手走... 查看详情

golang✔️实战✔️聊天室☢️建议手收藏☢️(代码片段)

【Golang】✔️实战✔️聊天室☢️建议手收藏☢️概述服务端实现客户端实现日志概述今天我们会结合之前几节课的知识来综合实战一下,实现一个聊天室.服务端实现运行的时候我们可以开启一个服务端和N个客户端,来实现聊天... 查看详情

golang✔️实战✔️聊天室☢️建议手收藏☢️(代码片段)

【Golang】✔️实战✔️聊天室☢️建议手收藏☢️概述服务端实现客户端实现日志概述今天我们会结合之前几节课的知识来综合实战一下,实现一个聊天室.服务端实现运行的时候我们可以开启一个服务端和N个客户端,来实现聊天... 查看详情

opencv⚠️高手勿入!半小时学会基本操作16⚠️分水岭算法(代码片段)

【OpenCV】⚠️高手勿入!半小时学会基本操作16⚠️分水岭算法概述分水岭算法距离变换连通域分水岭代码实战概述OpenCV是一个跨平台的计算机视觉库,支持多语言,功能强大.今天小白就带大家一起携手走进OpenCV的世界.(第16课)分水... 查看详情

☀️python+opencv常用函数☀️《❤️记得关注专栏❤️》

☀️Python+opencv常用函数☀️《❤️记得关注专栏❤️》目录 查看详情

☀️python+opencv图像处理☀️《❤️记得关注专栏❤️》

☀️Python+opencv图像处理☀️《❤️记得关注专栏❤️》 查看详情