基于OpenCV的数码管数字识别

2023-10-27

利用OpenCV可实现工业仪表设备的读数识别。仪表一般可分为两:数字式仪表和指针式仪表,本博文主要介绍一下数字式仪表识别的关键技术。下图是用软件模拟的数码管图片,本文识别的也就是图中的数字。待识别数字

一、图像定位
在实际的应用场景中,拍摄到的仪表区域很有可能会包含多余的背景部分,一个比较简单的解决方法是在拍摄时先行设定一个边界区域,提醒拍摄者将待识别的内容限制在区域中。后期识别时直接提取边界区域内的信息进行识别。

二、图像预处理
图像预处理的内容包括灰度化、二值化、腐蚀(或膨胀)、轮廓提取以及数字分割等。

1.灰度化
灰度化的目的是将图片从RGB的格式转为单通道,像素值为~255范围内的灰度图。

#define picture   "test4.png"  // filepath 
...
Mat image_org = imread(picture, IMREAD_COLOR);
imshow("image_org", image_org);  // read RGB image
Mat image_gry = imread(picture, IMREAD_GRAYSCALE);
if (image_gry.empty()) // read RGB image
    return -1;
imshow("image_gry", image_gry);

如下图所示:
这里写图片描述

2.二值化
二值化操作将灰度图变为像素值为0或者255的二值化图像,阈值可以根据图片的实际需求设定,要求是能将背景和数字分开。

Mat image_bin;
threshold(image_gry, image_bin, 50, 255, THRESH_BINARY); // convert to binary image
imshow("image_bin", image_bin);

二值化效果如下,threshold()函数中第二个形参选取的是THRESH_BINARY,因此图像变成黑底白字的效果。注意:此时图片背景文字的颜色直接影响后期的处理。
此形参的取值详见: cv::ThresholdTypes
这里写图片描述

3.腐蚀/膨胀
数字式仪表大部分采用八段式数码管,因此数字是不连续的。因此,在数字分割提取之前需要采取一定的操作使得数字的笔画连接起来,以防止数字被割裂而无法识别。腐蚀膨胀操作就可以解决这个问题。需要注意的是,腐蚀膨胀是对于白色部分而言的,膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。
现在的字体是白色的,如果想要让字体连续,就需要进行膨胀。 但也不能过度膨胀,否则会使得相邻数字连接起来,无法分割。

Mat image_dil;
Mat element = getStructuringElement(MORPH_RECT, Size(20, 20)); // 膨胀
dilate(image_bin, image_dil, element);
imshow("image_dil", image_dil);

膨胀的效果如下:
这里写图片描述

4.轮廓提取
每个数字连通后,即可进行轮廓提取,找个每个数字的轮廓位置信息。轮廓信息都存储在contours_out中。然后根据轮廓拟合成矩形轮廓。但是注意位置信息存储的顺序不是按照实际的坐标位置存储的,需要重新排序。本文是根据轮廓所在列信息(x)进行重排。

vector<vector<Point> > contours_out;
vector<Vec4i> hierarchy;
findContours(image_dil, contours_out, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);  
// re-arrange location according to the real position in the original image 
const size_t size = contours_out.size();
vector<Rect> num_location;
for (int i = 0; i < contours_out.size(); i++)
{    
    num_location.push_back(boundingRect(Mat(contours_out[i])) );// 转换为矩形轮廓
}
sort(num_location.begin(), num_location.end(), cmp); // 重排轮廓信息

bool cmp(const Rect& a, const Rect& b)
{
    if (a.x < b.x)
        return true;
    else
        return false;
}

查找到的轮廓如下图所示:
这里写图片描述
在实际的应用场景中,图像不可避免地存在一些噪声部分,此时噪声部分也可能被提取出来,因此在得到所有轮廓后还需进行滤波处理,除去噪声轮廓。上图中包括噪声图像为数字3、4以及7、8之间的白色部分,需要滤除。

5.数字分割
根据提取的矩形轮廓信息,可分割出单独的数字进行识别。

for (int i = 0; i < contours_out.size(); i++)
{
    if (!IsAllWhite(image_dil(num_location.at(i)))) // 是否为数字
    {
        tube.push_back(image_dil(num_location.at(i)));
        imshow(string(_itoa(tube_num, rectnum, 10)), tube.at(tube_num));
        tube_num++;
    }
}

分割出的数字效果如下:
这里写图片描述

三、数字识别

1.穿线法
数字式仪表的数字都是八段数码管式数字,都是横平竖直的笔画,没有弧度,可以考虑用割线进行识别,原理图如下。将数字区域(数字1除外)分割成六个部分,扫描个部分的像素点,判断该区域内是否存在笔画(a,b,c,d,e,f,g),最后根据二进制的规则可推断出数字的值。除数字1外,剩余数字分割后图像长宽比都接近,唯独数字1图像的长宽比相对要大一些,可设定合理的阈值来确定数字1的图像。
这里写图片描述

int TubeIdentification(Mat inputmat) // 穿线法判断数码管a、b、c、d、e、f、g、
{
    int tube = 0;
    int tubo_roi[7][4] =
    {
        { inputmat.rows * 0 / 3, inputmat.rows * 1 / 3, inputmat.cols * 1 / 2, inputmat.cols * 1 / 2 }, // a
        { inputmat.rows * 1 / 3, inputmat.rows * 1 / 3, inputmat.cols * 2 / 3, inputmat.cols - 1     }, // b
        { inputmat.rows * 2 / 3, inputmat.rows * 2 / 3, inputmat.cols * 2 / 3, inputmat.cols - 1     }, // c
        { inputmat.rows * 2 / 3, inputmat.rows - 1    , inputmat.cols * 1 / 2, inputmat.cols * 1 / 2 }, // d
        { inputmat.rows * 2 / 3, inputmat.rows * 2 / 3, inputmat.cols * 0 / 3, inputmat.cols * 1 / 3 }, // e
        { inputmat.rows * 1 / 3, inputmat.rows * 1 / 3, inputmat.cols * 0 / 3, inputmat.cols * 1 / 3 }, // f
        { inputmat.rows * 1 / 3, inputmat.rows * 2 / 3, inputmat.cols * 1 / 2, inputmat.cols * 1 / 2 }, // g
    };

    if (inputmat.rows / inputmat.cols > 2)   // 1 is special, which is much narrower than others
    {
        tube = 6;
    }
    else
    {
        for (int i = 0; i < 7; i++)
        {

            if (Iswhite(inputmat, tubo_roi[i][0] , tubo_roi[i][1], tubo_roi[i][2], tubo_roi[i][3]))
                tube = tube + (int)pow(2, i);
        }
    }

    switch (tube)
    {
        case  63: return 0;  break;
        case   6: return 1;  break;
        case  91: return 2;  break;
        case  79: return 3;  break;
        case 102: return 4;  break;
        case 109: return 5;  break;
        case 125: return 6;  break;
        case   7: return 7;  break;
        case 127: return 8;  break;
        case 111: return 9;  break;

        default: return -1;
    }
}

2.KNN算法
使用K近邻法对数字图像进行分类,若采用此方法,首先需要收集数码管数字的数据集。需要注意的时,建立KNN模型时,对于数据集进行了一些操作,因此需要对待分类的图像进行相同的操作,否则识别的准确率不高。特别要注意数据集和待识别图像中背景和字体的颜色是否一致。

char trainfile[100];
Mat traindata, trainlabel, tmp;
for (int i = 0; i < TRAINDATANUM; i++)
{
    sprintf(trainfile, "%s\\%d.jpg", TRAINPATH, i); // TRAINPATH可能需要根据实际修改
    tmp = imread(trainfile, IMREAD_GRAYSCALE);   // 读取数据集图像信息
    threshold(tmp, tmp, 50, 255, THRESH_BINARY);
    resize(tmp, tmp, Size(NORMWIDTH, NORMHEIGHT));
    traindata.push_back(tmp.reshape(0, 1));
    trainlabel.push_back(i);  // 附件标签信息
}
traindata.convertTo(traindata, CV_32F);

int K = 1;
Ptr<TrainData> tData = TrainData::create(traindata, ROW_SAMPLE, trainlabel);
Ptr<KNearest> knn = KNearest::create();
knn->setDefaultK(K);
knn->setIsClassifier(true);
knn->train(tData);
for (int i = 0; i < tube_num; i++)
{
    resize(tube.at(i), tube.at(i), Size(NORMWIDTH, NORMHEIGHT));
    tube.at(i) = tube.at(i).reshape(0, 1);
    tube.at(i).convertTo(tube.at(i), CV_32F);
    int r = knn->predict(tube.at(i));   //对所有行进行预测
    cout << r << endl;
}

代码链接:
数码管数字识别–穿线法
数码管数别字识–KNN算法

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于OpenCV的数码管数字识别 的相关文章

  • OpenCV 读取视频文件时内存不足

    此示例从文件中读取视频cv2 VideoCapture在 python OpenCV 中内存不足 import cv2 cap cv2 VideoCapture file mp4 while True ret frame cap read
  • Opencv 运动检测与跟踪

    我需要在网络摄像头的视频帧中进行强大的运动检测和跟踪 背景总是一样的 目的是识别物体的位置 如果可能的话没有阴影 但并不急于去除阴影 我已经尝试过用于背景减法和阈值化的opencv算法 但这仅取决于一个图像作为背景 如果背景的亮度 或相机自
  • 将yuv420p原始数据转换为opencv图像

    我有来自 rtmp 服务器的原始数据 像素格式为 yuv420p 我使用管道来读取数据 但我不知道如何将原始数据解码为图像 command ffmpeg command extend loglevel fatal i rtmp localh
  • 在OpenCV中将YUV转换为BGR或RGB

    我有一个电视采集卡 其输入内容为 YUV 格式 我在这里看到了与此问题类似的其他帖子 并尝试尝试所述的所有可能的方法 但它们都没有提供清晰的图像 目前最好的结果是 OpenCVcvCvtColor scr dst CV YUV2BGR 函数
  • python openCV 中的人口普查变换

    我开始在一个与立体视觉相关的项目中使用 openCV 和 python 我找到了关于使用 openCV 在 C 中进行人口普查转换的文档页面 link http docs opencv org 3 1 0 d2 d7f namespacec
  • 使用 pygtk3 将 GUI 窗口添加到 python opencv2 程序

    我已经使用Python和Opencv2完成了一个程序 现在 我想向我的程序添加一个 GUI 窗口 我对 PyGtk3 有一些经验 因此 我修改了代码以采用 PyGtk3 但是 我遇到了错误 因此 我尝试了一个简单的程序来找出实际的错误 我的
  • 使用 openCV 对图像中的子图像进行通用检测

    免责声明 我是计算机视觉菜鸟 我看过很多关于如何在较大图像中查找特定子图像的堆栈溢出帖子 我的用例有点不同 因为我不希望它是具体的 而且我不确定如何做到这一点 如果可能的话 但我感觉应该如此 我有大量图像数据集 有时 其中一些图像是数据集的
  • 如何将 Opencv VideoWriter 与 GStreamer 结合使用?

    我正在尝试使用 Opencv VideoWriter 传输 h264 流 以使用 VideoCapture 将其传输到网络上的另一台电脑上 但是 我被困在 VideoWriter 上 执行此代码会返回错误 并且 out isOpened 始
  • Python 中的 Lanczos 插值与 2D 图像

    我尝试重新缩放 2D 图像 灰度 图像大小为 256x256 所需输出为 224x224 像素值范围从 0 到 1300 我尝试了两种使用 Lanczos 插值来重新调整它们的方法 首先使用PIL图像 import numpy as np
  • 如何绘制每个分割对象的轮廓

    我应用分水岭分割来检测触摸对象 这样做效果很好 现在 我想绘制每个对象的轮廓 这样我就可以获得它们的长度 面积 矩等 但是分割结果中的对象仍然是触摸的 所以 我没能画出每一个的轮廓 如何绘制每个对象的轮廓 include
  • Opencv C++ 检测并裁剪图像上的白色区域

    我在网上搜索过 已经找到了一些方法来完成我想要的事情 但是与我需要的相比 这些方法的效率较低 我有一个 kinect 使用 Microsoft SDK 当前正在获取一个移除背景的人 将结果保存在 3 通道 Mat 中 并将该人从背景中移除
  • 在 HSV 颜色空间内定义组织学图像掩模的颜色范围(Python、OpenCV、图像分析):

    为了根据颜色将组织学切片分成多个层 我修改了 OpenCV 社区提供的一些广泛分布的代码 1 我们的染色程序用不同的颜色标记组织横截面的不同细胞类型 B 细胞为红色 巨噬细胞为棕色 背景细胞核为蓝色 I m interested in se
  • opencv - 在图像中绘制轮廓

    我正在尝试在图像周围绘制轮廓 我可以看到找到了轮廓 但无法绘制轮廓 轮廓的颜色似乎是两种 黑色和白色 颜色中的一种 import cv2 import numpy as np import matplotlib pyplot as plt
  • 每当使用 import cv2 时 OpenCV 都会出错

    我在终端上使用 pip3 install opencv contrib python 安装了 cv2 并且它工作了 但是每当我尝试导入 cv2 或运行导入了 cv2 的 vscode 文件时 在 python IDLE 上它都会说 Trac
  • 如何根据图像中的对象大小(以像素为单位)来测量现实世界中的对象大小(例如英寸、厘米等)?

    我计算了物体的大小pixel来自包含对象的图像 我想测量现实世界中物体的大小 有没有办法找出乘数来测量实际尺寸 我目前正在使用python以便实施 通常 您将使用相机获取图像 该相机通过镜头将 3 维场景投影到 2 维传感器上 垂直 高度
  • 使用 ffmpegframerecorder android 保存视频

    我正在尝试保存该视频并收到此错误 我添加了两个jar文件1 javacv 2 javacpp and for 相机预览我用过opencv 我在这里添加代码 公共无效运行 while true Bitmap bmp null synchron
  • 检测霍夫圆android

    我正在尝试使用 android 检测圆圈 我成功实现了检测线算法 但在尝试绘制霍夫圆算法时没有显示任何内容 这是我的代码 Mat thresholdImage new Mat getFrameHeight getFrameHeight 2
  • FindFundamentalMatrix 未找到基本矩阵

    我正在尝试使用以下方法恢复相机的运动 基本矩阵 以及给出的算法维基百科 https en wikipedia org wiki Fundamental matrix computer vision 为了 我需要找到基本矩阵 我在用OpenC
  • 使用 Opencv 屏蔽水平线和垂直线

    我正在尝试删除该图像中的水平线和垂直线 以便拥有更清晰的文本区域 我正在使用下面的代码 它遵循这个guide https docs opencv org 3 2 0 d1 dee tutorial moprh lines detection
  • 将浮点数组图像转换为可用于 opencv 的格式

    我想知道是否有一种简单的方法可以将浮点数组图像转换为 iplimage 可以用opencv来处理 当然 我可以创建一个具有相同大小的空 iplimage 然后将浮点数组图像中的每个像素复制到空 iplimage 但是是否有更优雅的解决方案

随机推荐

  • Python基础复习总结

    文章目录 Python基础复习 Python的下载与安装 标识符 关键字 模块 条件 循环语句 if elif else for循环 while循环 成员测试 Python中的数据类型 序列 列表 list 元组 tuple 集合 set
  • javascript数据类型number、string和布尔

    number数字类型 计算机number是有一个范围的 2的53次方 2的53次方 注意书写顺序 例如一个商品17 45 买3个打9折 以下两种写法保留两位小数结果是不相同的 var obj age 20 var num 17 45 con
  • 8-使用QT5的鼠标事件和滚轮事件

    使用QT5的鼠标事件和滚轮事件 完成鼠标左键拖动窗口 双击全屏 滚轮放大缩小窗口大小 这里使用的是QMouseEvent类里面的鼠标事件 通常进行重定义部件的鼠标事件处理函数来实现自定义的内容操作 同样 鼠标滚轮操作是利用QWheelEve
  • 三个可替代“迅雷”的下载软件,速度超快!

    今天推荐可以替代迅雷的软件 那些你使用迅雷无法下载的资源 现在都可以下载了哦 1 qBittorrent 解压提供的安装包 然后双击 qbittorrent exe 找到你要下载的磁力链接 点击右上角 添加下载任务 设置你的下载路径 点击
  • K8S控制器Deployment

    简述 Deployment为Pod和ReplicaSet提供了一个声明式定义 declarative 方法 用来替代以前的ReplicationController来方便的管理应用 典型的应用场景包括 定义Deployment来创建Pod和
  • 关于搭建测试环境(详细)

    简述搭建测试环境 本人呢 是一名测试人员 以前工作的时候我们的测试环境都是网管 运维 帮我们管理的 顶多也就在tomcat下部署项目包 还是操作几个比较简单的指令 前不久就自己搭了套测试环境 然后也没事总结了一下 大致相同 可能存在个别差异
  • k8s lifecycle——poststart和prestop

    1 lifecycle的声明 lifecycle postStart exec command bin sh c sleep 100 preStop exec command bin sh c sleep 100 2 poststart 容
  • Nginx修改文件配置--配置本地网址

  • seata-server-1.5.2的环境搭建

    配置文件位置 使用的是nacos和mysql数据库 简单部署在Win10上 Linux上配置修改相同 启动命令不同 找到 seata server 1 5 2 seata conf目录下的application yml和applicatio
  • win10 vscode+clangd代码提示+cmake+mingw编译器和调试器

    win10 vscode clangd代码提示 cmake mingw编译器和调试器 前言 第一步 把cmake mingw llvm win64安装好 安装好vscode必备的插件 利用cmake构建一个项目 利用vscode的launc
  • 移动端VUE实现一周课程表

    效果图 点击课程弹出课程详情 代码 使用嵌套的v for循环去实现
  • m3u8文件中的 m3u8标签与属性说明

    EXTM3U 每个M3U文件第一行必须是这个tag 请标示作用 EXT X VERSION 3 该属性可以没有 EXT X MEDIA SEQUENCE 140651513 每一个media URI在PlayList中只有唯一的序号 相邻之
  • ubuntu18.04安装cuda、cudnn、pytorch-gpu

    cuda安装 参考博客 https blog csdn net mbdong article details 121926316 https mp weixin qq com s ZTzfC7xp8PVMvOONVIiK6g https b
  • Vue简单实例——Vuex代码实现

    简单介绍 上一篇我们介绍了Vuex的理论 这一章我们开始说明Vuex在代码方面的实现 基本使用 要想使用Vuex首先要进行下载 在下载的时候需要注意 如果你使用的vue2的框架 需要使用vuex的3版本 如果使用的是vue3的框架 才可以使
  • Open3D DbScanClustering聚类算法及聚类分簇可视化及存储

    DBSCAN聚类算法 是基于密度的聚类算法 该算法需要两个参数 labels np array pcd cluster dbscan eps 0 02 min points 10 print progress True 入参 eps 定义到
  • Java实体类与byte数组相互转换

    1 使用ByteArrayStream 和 ObjectStream public abstract class ByteConvert public byte getByte try ByteArrayOutputStream out n
  • Qt技巧:sqlite数据库 判断表是否存在

    m dbTest QSqlDatabase addDatabase QSQLITE m dbTest setDatabaseName sqlite db if m dbTest open qDebug lt lt database succ
  • Android安卓期末大作业 新闻app 实现注册登录增删改查功能

    Android安卓期末大作业 新闻app 文末附下载链接 app情况如下图所示 点我下载 https download csdn net download weixin 43474701 75953692
  • 泛微oa明细表添加按钮_关于E8,这些快捷方式你必须知道

    摘要 快捷方式不只是快 更能带来酣畅用户体验 本期场景为您带来E8快捷方式精选 看着那些大神们手指翻飞 在键盘上灵活起舞 说实话 你有过几分羡慕吗 快捷输入这东西 有时候真是少不了 虽然只是几秒几十秒的差异 但用户体验着实不同 试想 当你正
  • 基于OpenCV的数码管数字识别

    利用OpenCV可实现工业仪表设备的读数识别 仪表一般可分为两 数字式仪表和指针式仪表 本博文主要介绍一下数字式仪表识别的关键技术 下图是用软件模拟的数码管图片 本文识别的也就是图中的数字 一 图像定位 在实际的应用场景中 拍摄到的仪表区域