首页 > 其他分享 >【进阶OpenCV】 (8)--摄像头操作--->识别文档内容

【进阶OpenCV】 (8)--摄像头操作--->识别文档内容

时间:2024-10-11 21:18:32浏览次数:3  
标签:进阶 -- image cv2 --- np 轮廓 pts rect

文章目录

摄像头操作

本篇我们来介绍,如何打开摄像头来识别文档。

思路

  1. 打开摄像头。
  2. 描绘出摄像头识别画面中的所有轮廓
  3. 那么,轮廓有了,如何找到独属于文档的轮廓呢?我们知道,一般的文档都是长方形的,利用这一点,我们可以通过轮廓近似的方法,看看哪些轮廓是可以通过四点定位的,从而取出文档轮廓。
  4. 识别之后,通过透视变换方法,将文档规整的单独展示出来。

1. 打开摄像头

通过**cv2.VideoCapture()**方法,当括号内为0时,打开电脑摄像头;为1时,打开外接摄像头。

cap = cv2.VideoCapture(0) # 打开摄像头
if not cap.isOpened(): # 打开失败‘
    print("Cannot open camera")
    exit()

2. 识别画面预处理

使用**cap.read()**方法读取摄像头画面,将画面转化为灰度图,进行高斯滤波去除噪声:

def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(60)
    
while True:
    flag = 0 # 用于标识,当前是否检测到文件
    ret,image = cap.read() # 如果正确读取,ret为True
    orig = image.copy()
    if not ret:
        print("不能读取摄像头")
        break
    cv_show("image",image)

    gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 将图像转化为灰度图
    # 预处理
    gray = cv2.GaussianBlur(gray,(5,5),0) # 高斯滤波
    edged = cv2.Canny(gray,75,200)
    cv_show('1',edged)

注意!!!:以下每点操作都在主循环while True中。

3. 轮廓检测

通过cv2.findContours()方法查询轮廓,并将其在原图上描绘出来:

# 轮廓检测
cnts = cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[1]
cnts = sorted(cnts,key=cv2.contourArea,reverse=True)[:3]
image_contours = cv2.drawContours(image,cnts,-1,(0,255,0),2)
cv_show("image_contours",image_contours)

4. 轮廓近似

遍历每一个检测到的轮廓,使用**cv2.approxPolyDP()**方法对其进行轮廓近似,因为文档是长方形的,接着将近似轮廓只需要四个点就组成的轮廓取出,意味着成功识别到了文档:

# 遍历轮廓
for c in cnts:
    # 计算轮廓近似
    peri = cv2.arcLength(c,True) # 计算轮廓的周长

    approx = cv2.approxPolyDP(c,0.05 * peri,True) # 轮廓近似
    # C表示输入的点集
    # epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确参数
    # True表示封闭的
    area = cv2.contourArea(approx)

    # 4个点的时候就拿出来
    if area > 20000 and len(approx) == 4:
        screenCnt = approx
        flag = 1
        print(peri,area)
        print('检测到文档')
        break

5. 透视变换

率先将透视变换的方法通过函数形式编写出来以便于调用(定位到四个角点,然后将其透视变换到一个矩阵上):

5.1 定义order_point(pts)方法:

用于将给定的四个点(通常是从图像中检测到的轮廓点或角点)按照特定的顺序排列:左上角(tl)、右上角(tr)、右下角(br)和左下角(bl)。

过程

  • 首先,计算每个点坐标的和 s,通过 np.argmin(s) 和 **np.argmax(s)**找到 y 值最小(最上)和最大(最下)的两个点,分别作为矩形的顶部和底部点。

  • 然后,计算每对相邻点之间 x 坐标的差 diff,通过 np.argmin(diff)np.argmax(diff) 找到 x 值变化最小(最左,即左侧边界上的点,假设点按顺时针或逆时针顺序给出)和最大(最右,即右侧边界上的点)的两个点,分别作为矩形的左侧和右侧点。

  • 输出:rect是一个形状为 (4, 2) 的 NumPy 数组,包含了按左上角、右上角、右下角、左下角顺序排列的四个点。

def order_point(pts):
    rect = np.zeros((4,2),dtype="float32")
    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

5.2 定义four_point_transform(image,pts)方法:

这个函数使用 order_point函数得到的点来对输入图像进行透视变换,使得这四个点映射到一个矩形上。

  • 输入:image是要进行透视变换的输入图像,pts是图像中检测到的四个点的坐标。

  • 过程

  1. 首先,调用 order_point(pts) 来获取按特定顺序排列的四个点(tl, tr, br, bl)。

  2. 然后,计算这四个点形成的矩形的宽度和高度,以确保变换后的图像能够包含整个矩形区域。

  3. 接着,定义一个目标矩形 dst,其四个角点映射到变换后的图像的 (0,0)、(maxwidth-1,0)、(maxwidth-1,maxheight-1) 和 (0,maxheight-1) 位置。

  4. 使用 OpenCV 的 **cv2.getPerspectiveTransform(rect, dst)**函数计算透视变换矩阵 M

  5. 最后,使用 **cv2.warpPerspective(image, M, (maxwidth, maxheight))**对输入图像进行透视变换,得到变换后的图像。

  • 输出warped是经过透视变换后的图像。
def four_point_transform(image,pts):
    # 获取输入坐标点
    rect = order_point(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

5.3 代码应用

将透视变换后的图像进行二值化处理,是内容呈现更清晰:

if flag == 1:
    # 展示结果
    image_contours = cv2.drawContours(image,[screenCnt],0,(0,255,0),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_RGB2GRAY)
    ref = cv2.threshold(warped,220,255,cv2.THRESH_BINARY)[1]
    # ref = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    cv_show("ref",ref)

6. 关闭图像窗口

对于视频画面的捕获,每一帧保存,需要通过**cap.release()**方法释放(写在主循环外哦!):

cap.release() # 释放捕获器-
cv2.destroyAllWindows() # 关闭图像窗口

7. 完整代码展示

import cv2
import numpy as np

def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(60)

def order_point(pts):
    rect = np.zeros((4,2),dtype="float32")
    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_point(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

if __name__ == '__main__':
    cap = cv2.VideoCapture(0) # 打开摄像头
    if not cap.isOpened(): # 打开失败‘
        print("Cannot open camera")
        exit()

    while True:
        flag = 0 # 用于标识,当前是否检测到文件
        ret,image = cap.read() # 如果正确读取,ret为True
        orig = image.copy()
        if not ret:
            print("不能读取摄像头")
            break
        cv_show("image",image)

        gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 将图像转化为灰度图
        # 预处理
        gray = cv2.GaussianBlur(gray,(5,5),0) # 高斯滤波
        edged = cv2.Canny(gray,75,200)
        cv_show('1',edged)

        # 轮廓检测
        cnts = cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[1]

        cnts = sorted(cnts,key=cv2.contourArea,reverse=True)[:3]
        image_contours = cv2.drawContours(image,cnts,-1,(0,255,0),2)
        cv_show("image_contours",image_contours)

        # 遍历轮廓
        for c in cnts:
            # 计算轮廓近似
            peri = cv2.arcLength(c,True) # 计算轮廓的周长

            approx = cv2.approxPolyDP(c,0.05 * peri,True) # 轮廓近似
            # C表示输入的点集
            # epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确参数
            # True表示封闭的
            area = cv2.contourArea(approx)

            # 4个点的时候就拿出来
            if area > 20000 and len(approx) == 4:
                screenCnt = approx
                flag = 1
                print(peri,area)
                print('检测到文档')
                break
        if flag == 1:
            # 展示结果
            image_contours = cv2.drawContours(image,[screenCnt],0,(0,255,0),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_RGB2GRAY)
            ref = cv2.threshold(warped,220,255,cv2.THRESH_BINARY)[1]
            # ref = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
            cv_show("ref",ref)

cap.release() # 释放捕获器-
cv2.destroyAllWindows() # 关闭图像窗口

总结

本篇介绍了:

如何通过打开摄像头,识别文档。

打开摄像头读取每一帧 ----> 识别轮廓 ----> 通过轮廓近似找到文档 -----> 透视变换取出文档 ----->二值化文档,使文档更清晰。

标签:进阶,--,image,cv2,---,np,轮廓,pts,rect
From: https://blog.csdn.net/m0_74896766/article/details/142863605

相关文章

  • 【论文解读】KVQ: Kwai Video Quality Assessment for Short-form Videos
    原文链接:https://arxiv.org/pdf/2402.07220级别:IEEE/CVF机构:快手作者:YitingLu时间:2024可下载地址:KVQ:KwaiVideoQualityAssessmentforShort-formVideos摘要UGC(用户生成内容)短视频平台,如快手(Kwai)和抖音(TikTok),已经成为一种新兴且不可替代的主流媒体形式。......
  • C++常用库函数
    大小写转换islower/isupper函数用于检查一个字符是否为小写或大小字母,需要包含头文件<cctype>,也可以包含万能头文件<bits/stdc++.h>.函数返回值类型为bool类型。intmain(){ charch1='A'; charch2='b'; //使用islower函数判断是否为小写字母 if(islower......
  • 问题定位总结:java空字符
    在线上业务中,有个校验,校验用户输入的信息与现在表里存的信息数据是否一致。比较时忽略首尾的空字符。但收到用户反馈,在页面填入的数据和表里存的数据一致。校验却不通过。假设表里存的是“CSDN专业开发者社区”,用户填写的是“CSDN专业开发者社区   ”,后面带有空格。对于用......
  • 计算机组成原理中的指令流水线
    这道题目考察的是计算机组成原理中的指令流水线(InstructionPipelining)的概念和计算方法。知识点:指令流水线指令流水线是一种提高CPU执行指令效率的技术。它将指令的执行过程分解为多个阶段,每个阶段完成不同的任务,并且这些阶段可以并行执行。这样可以显著减少执行一条指令所需的......
  • 新歌单
    1._君の名は(马一鸣)我们会像风壁上观寂寞烟火慢慢辞九门回忆盗墓笔记·十年人间.星星一般的一天打上花火LoserLemon地球仪红莲华onlymyrailgun2.zhengchenxi(郑晨熙)onmyway我用什么把你留住爱人错过半岛铁盒3.QEDQEDQED(赵思源)使一颗心免于哀伤GoneAngle......
  • [Paper Reading] HPT: Scaling Proprioceptive-Visual Learning with Heterogeneous P
    目录ScalingProprioceptive-VisualLearningwithHeterogeneousPre-trainedTransformersTL;DRMethodStemTrunkLossHeadExperiment训练资源效果可视化总结与发散相关链接资料查询ScalingProprioceptive-VisualLearningwithHeterogeneousPre-trainedTransformersScaling......
  • 解密prompt系列40. LLM推理scaling Law
    OpenAI的O-1出现前,其实就有已经有大佬开始分析后面OpenAI的技术路线,其中一个方向就是从Pretrain-scaling,Post-Train-scaling向InferenceScaling的转变,这一章我们挑3篇inference-scaling相关的论文来聊聊,前两篇分别从聚合策略和搜索策略来优化广度推理,最后一篇全面的分析了各类广......
  • 区间dp板子
    比较简单的dp,但是建模可能会比较困难。以P1775石子合并(弱化版)为例(https://www.luogu.com.cn/problem/P1775)思路:要求1-n的石子合并的代价,可以看成小的区间问题,化为1-k+k-n的两个区间。然后就有递推式子:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[j]-w[i-1]。编......
  • 计算机存储器的容量计算和地址转换
    这道题目考查的是计算机存储器的容量计算和地址转换。具体来说,它涉及到以下几个知识点:地址转换:将十六进制地址转换为十进制数,以计算出地址范围内的总字节数。存储器容量计算:根据存储器芯片的容量和地址范围内的总字节数,计算出需要多少片芯片来构成指定的内存区域。详细内容:......
  • Java-Exception与RuntimeException
    ......