【openCV】手写算式识别

news2024/11/15 19:39:54

OpenCV 机器学习库提供了一系列 SVM 函数和类来实现 SVM 模型的训练和预测,方便用户实现自己的 SVM 模型,并应用于分类问题。本文主要介绍使用 openCV 实现手写算式识别的工作原理与实现过程。

目录

1 SVM 模型

1.1 SVM 模型介绍

1.2 SVM 模型原理

2 手写算式识别

2.1 字符识别

2.2 算式识别


1 SVM 模型

1.1 SVM 模型介绍

        SVM 是支持向量机(Support Vector Machine)的英文缩写,是统计学习理论中一种重要的分类方法,其早期工作来自前苏联 Vladimir N. Vapnik 和 Alexander Y. Lerner 在1963年发表的研究。

        1995年,Corinna Cortes 和 Vapnik 提出了软边距的非线性 SVM 并将其应用于手写字符识别问题,为 SVM 在其他领域的应用提供了参考。

SVM 的优点主要包括:

​    1)具有较好的可解释性。SVM 的决策函数和支持向量清晰,易于理解。

​    2)适用性广泛。SVM 能够应用于多种数据类型和领域,如文本分类、图像识别和生物信息学等。

​    3)鲁棒性强。SVM 对训练数据中的噪声和异常点具有较强的容错能力,能有效处理输入数据中的噪声。

​    4)适合高维数据。通过核函数,SVM 能够将低维空间的非线性问题映射到高维空间,进行线性划分,从而解决复杂的非线性问题。

​    5)可控制的过拟合。通过调整正则化参数和松弛变量,SVM 可以控制模型的复杂度,有效避免过拟合问题。

​    6)避免陷入局部最优解。使用结构风险最小化原则,使得 SVM 能够更好地避免陷入局部最优解,并具有较低的泛化误差。

1.2 SVM 模型原理

        在二分类问题中,给定输入数据和学习目标: X=\{ X_1, X_2, ... , X_N\}y \in \{-1, 1\},若存在决策边界(decision boundary)

\omega ^T X + b = 0

将样本按类别分开,则称该分类问题是线性可分的(Linear Separable)。

        按照统计学习理论,分类器在经过学习新数据时会产生风险,风险的类型分为经验风险和结构风险:

式中 f 表示分类器,经验风险由损失函数定义,描述了分类器所给出的分类结果的准确程度;结构风险由分类器参数矩阵的范数定义,描述了分类器自身的复杂程度以及稳定程度。

        复杂的分类器容易过拟合,因此是不稳定的。通过最小化经验风险和结构风险的线性组合以确定其模型参数:

式中 C 是正则化参数,当 p = 2 时,该式被称为 L_2 正则化。

​    对于线性可分问题,SVM 经验风险为 0,SVM 模型简化为最小化结构风险,由于点到超平面的距离反比于 || ω ||,因此模型可解释为最大化样本到超平面的最小距离,

即最优超平面距离给定的每个样本尽可能远。

2 手写算式识别

2.1 字符识别

        OpenCV 机器学习库提供了一系列 SVM 函数和类来实现 SVM 模型的训练和预测,可以很方便地实现用户自定义的分类模型。

使用 OpenCV 实现 SVM 模型的基本步骤如下:

    (1)创建模型。使用 cv2.ml.SVM_create() 创建 SVM 模型,使用 setKernel() 指定核函数;

    (2)初始化模型参数。使用 setC() 和 setGamma() 设置参数的初始值;

    (3)模型训练。使用 train() 函数,以及向量化的样本和分类标签,训练模型;

    (4)模型评估。使用 predict() 预测新样本,并统计正确率;

    (5)模型保存。使用 save() 保存模型,文件格式为 *.dat 。

        在手写算式的字符识别中,需要识别数字 0 ~ 9,以及 +,-,×,÷,(,)和 = 共 17 种字符。SVM 模型的输入样本是字符图像向量化的结果,处理步骤包括:

      1)图像缩放。将字符图像统一成 28 × 28 大小;

      2)颜色反转。使用 cv2.bitwise_not() 函数实现颜色反转,便于后续步骤;

      3)去偏斜。使用 cv2.moments() 计算图像的矩,然后使用 cv2.warpAffine() 去偏斜;

      4)向量化。将图像按照十字划分成 4 个区域,计算每个区域的方向梯度直方图,拼接成一个向量。

参考链接:OpenCV: OCR of Hand-written Data using SVM

2.2 算式识别

        手写算式识别包括 3 个阶段:字符分割、图像预处理和字符识别。字符分割用于提取输入图像中的连续字符,图像预处理用于字符图像的特征化,字符识别用于图像与字符的对应。最后按照顺序拼接识别到的字符,就得到输出表达式。

#-*- Coding: utf-8 -*-

import cv2
import numpy as np
import gradio as gr

# 加载模型
model = cv2.ml.SVM_load('./svm_data.dat')
chars = '0123456789+-*/()='


SZ = 28
bin_n = 16 # Number of bins


def resize(src_img, size):
   # 获取原图像的宽、高
   h, w = src_img.shape

   if h >= size and w >= size:
      # 图像缩放
      dst_img = cv2.resize(src_img, (size, size), interpolation=cv2.INTER_CUBIC)
   elif h >= size:
      # 填充左右边缘
      dst_img = np.zeros(shape=(h, size), dtype=np.uint8)
      dst_img[:, (size-w)//2:(size-w)//2+w] = src_img
      dst_img = cv2.resize(dst_img, (size, size), interpolation=cv2.INTER_CUBIC)
   elif w >= size:
      # 填充上下边缘
      dst_img = np.zeros(shape=(size, w), dtype=np.uint8)
      dst_img[(size-h)//2:(size-h)//2+h, :] = src_img
      dst_img = cv2.resize(dst_img, (size, size), interpolation=cv2.INTER_CUBIC)
   else:
      # 填充四周
      dst_img = np.zeros(shape=(size, size), dtype=np.uint8)
      dst_img[(size-h)//2:(size-h)//2+h, (size-w)//2:(size-w)//2+w] = src_img

   return dst_img


def deskew(src_img):
   """Deskew the image using its second order moments"""
   m = cv2.moments(src_img)

   if abs(m['mu02']) < 1e-2:
      return src_img.copy()

   skew = m['mu11']/m['mu02']
   M = np.float32([[1, -skew, 0.5*SZ*skew], [0, 1, 0]])
   dst_img = cv2.warpAffine(src_img, M, (SZ, SZ), cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)

   return dst_img


def hog(image):
   gx = cv2.Sobel(image, cv2.CV_32F, 1, 0)
   gy = cv2.Sobel(image, cv2.CV_32F, 0, 1)
   mag, ang = cv2.cartToPolar(gx, gy)
   bins = np.int32(bin_n*ang/(2*np.pi)) # quantizing binvalues in (0, ..., 16)
   bin_cells = bins[:14,:14], bins[14:,:14], bins[:14,14:], bins[14:,14:]
   mag_cells = mag[:14,:14], mag[14:,:14], mag[:14,14:], mag[14:,14:]
   hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
   hist = np.hstack(hists) # hist is a 64bit vector
   return hist


def pre_process(src_img):
   """图像预处理"""
   img_resize = resize(src_img, SZ)
   img_invert = cv2.bitwise_not(img_resize) # 颜色翻转
   img_deskew = deskew(img_invert)
   hist = hog(img_deskew)
   return hist


def exprRecognize(src_img, filter_size):
   """手写算式识别"""
   # 灰度图
   gray = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY)

   # 二值化
   _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
   binary_inv = cv2.bitwise_not(binary)

   # 中值滤波
   filter_size = int(filter_size[0][0]) if filter_size else 3
   binary_f = cv2.medianBlur(binary_inv, filter_size)

   # 查找字符区域
   contours, _ = cv2.findContours(binary_f, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

   # 遍历所有区域,寻找最大宽度
   w_max = 0
   for cnt in contours:
      _, _, w, _ = cv2.boundingRect(cnt)
      if w > w_max:
         w_max = w

   # 遍历所有区域,拼接x坐标接近的区域
   char_dict = {}
   for cnt in contours:
      x, y, w, h = cv2.boundingRect(cnt)
      x_mid = x + w//2 # 计算中点位置

      if not char_dict.keys() or all(np.abs(z - x_mid) > w_max/1.5 for z in char_dict.keys()):
         char_dict[x_mid] = cnt
      else:
         for z in char_dict.keys():
            if np.abs(z - x_mid) <= w_max/1.5:
               char_dict[z] = np.concatenate((char_dict[z], cnt), axis=0) # 拼接两个区域

   # 按照中点坐标,对字符进行排序
   char_dict = dict(sorted(char_dict.items(), key=lambda item: item[0]))

   # 遍历所有区域,提取字符
   dst_img = []
   for _, cnt in char_dict.items():
      x, y, w, h = cv2.boundingRect(cnt)
      roi = binary[y:y+h, x:x+w]
      dst_img.append(roi)

   expr = ''
   for char in dst_img:
      hist = pre_process(char)
      hist = np.array(hist, dtype=np.float32)
      result = model.predict(hist.reshape(-1, 4*bin_n))[1]
      expr += chars[int(result[0])]

   return dst_img, expr, eval(expr.replace('=', ''))

if __name__ == "__main__":
   demo = gr.Interface(
      fn=exprRecognize,
      inputs=[
         gr.Image(label="input image"), 
         gr.Radio(['3x3', '5x5', '7x7'], value='3x3')
      ],
      outputs=[
         gr.Gallery(label="charset", columns=[3], object_fit="contain", height="auto"),
         gr.Text(label="expression"),
         gr.Text(label="result")
      ],
      live=True
   )

   demo.launch()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1534360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频素材库哪里找?推荐几个高质量的无水印视频素材网

在寻找创意优质素材的道路上&#xff0c;拥有一个好的导航仪至关重要。这不仅仅是关于找到一张图片或一个视频&#xff0c;而是关于发现那些能让你的项目闪耀的宝藏。今天&#xff0c;我将混合介绍国内外的素材网站&#xff0c;旨在为你提供一个全面的视角&#xff0c;同时尽量…

进阶二叉树

目录 二叉树 二叉搜索树 二叉搜索树的定义 二叉搜索树的操作 哈夫曼树 哈夫曼树的定义 哈夫曼树的构造 哈夫曼树的性质 平衡二叉树 平衡二叉树的定义&#xff1a; 平衡二叉树的插入调整 1.LL插入/LL旋转 2.RR插入/RR旋转 3.LR插入/LR旋转 4.RL插入/RL旋转 二叉树…

GPT-5什么时候会发布

1、前言 GPT-5预计将在夏季震撼发布&#xff0c;其卓越的性能和尚未公布的新功能已经引起了广泛关注。企业客户已有幸体验到了它的演示版&#xff0c;纷纷对其赞不绝口。OpenAI团队正在紧锣密鼓地进行最后的训练和优化&#xff0c;以确保GPT-5在发布时能够展现出最完美的状态。…

多线程(JUC, ReentrantLock, 原子类, 线程池, 信号量 Semaphore, CountDownLatch)

JUC Java.util.concurrent 包, 存放了并发编程相关的组件, 目的是更好的支持高并发任务 (多线程只是实现并发编程的一种具体方式 …) ReentrantLock 可重入互斥锁, 和 synchronized 定位类似, 用来实现互斥效果, 保证线程安全. synchronized 对对象加锁, 保护临界资源Reentreat…

吐槽FineDataLink工具Format函数处理日期转字符串格式的说明文档

一.背景 为公司师带徒的任务做些记录。 二.文档存在的问题 1.文档情况 FORMAT-格式转换- FineBI帮助文档 FineBI帮助文档 函数定义&#xff1a; FORMAT(object,format) formart的格式有哪些呢&#xff1f;我们截图看看&#xff1a; 2.文档说明不足问题 同事的需求是把时…

【C++ STL】string类最全解析(什么是string?string类的常用接口有哪些?)

目录 一、前言 二、什么是 string ? &#x1f4a6; string 类的基本概念 &#x1f4a6; string 类与 char * 的区别 &#x1f4a6; string 类的作用 &#x1f4a6; 总结 三、string 的常用接口详解 &#x1f4a6;string 类对象的默认成员函数 ① 构造函数(初始化) ② 赋值…

无管理员权限linux系统安装cmake

去官网下载所需版本&#xff1a;https://cmake.org/download/ 之后解压 安装 cd cmake-x.xx.xx ./bootstrap DESTDIRpath make install makebashrc中添加 export PATH"path/bin:$PATH"查看是否安装成功 source ~/.bashrc cmake --version安装成功 参考&#x…

关于汽车中网改装需要报备吗?(第二天)

车联网改造需要申报吗&#xff1f; 今天2022年10月20日&#xff0c;小编就给大家介绍一下车联网改装是否需要申报的相关知识。 让我们来看看。 汽车格栅改装无需申报。 这种年检可以直接通过。 您不必担心&#xff0c;因为汽车格栅对于实车的外观来说并不陌生&#xff0c;因此…

若依添加页面

背景&#xff1a;我想增加的是一个收支管理的页面 views中直接添加income文件夹&#xff0c;里面放着index.vue 网页的菜单中添加这个页面的菜单

基于springboot的医院医护人员排班系统

技术&#xff1a;springbootvuemysql 一、系统背景 本医护人员排班系统管理员&#xff0c;医护。管理员功能有个人中心&#xff0c;医院信息管理&#xff0c;医护信息管理&#xff0c;医护类型管理&#xff0c;排班信息管理&#xff0c;排班类型管理&#xff0c;科室信息管理&…

中国网怎么投稿,发稿流程,中国网发稿需要多少钱?(附中国网各频道价格明细)

中国网是一家专业的新闻媒体平台&#xff0c;拥有庞大的读者群体和高质量的新闻内容。很多企业和个人都希望能够在中国网上发布自己的文章或新闻&#xff0c;以增加曝光度和影响力。那么&#xff0c;中国网如何投稿&#xff1f;发稿流程是怎样的&#xff1f;又需要多少费用呢&a…

4款原创检测工具,帮你快速掌握文章质量

在如今信息爆炸的时代&#xff0c;人们在网上发布和浏览大量的文字内容。然而&#xff0c;随之而来的问题是大量的抄袭和非原创内容充斥着网络空间&#xff0c;这不仅影响了文章的质量&#xff0c;也降低了读者对内容的信任度。为了帮助作者和编辑快速提升文章质量&#xff0c;…

XCode升级错误:Command CompileC failed with a nonzero exit code 解决办法

升级完XCode之后&#xff0c;bulid失败&#xff0c;出现如下错误&#xff1a; 问题1&#xff1a; xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrunCommand Compi…

一命通关广度优先遍历

前言 在这篇文章之前&#xff0c;已对非线性结构遍历的另一种方法——深度优先遍历进行了讲解&#xff0c;其中很多概念词都是共用的。为了更好的阅读体验&#xff0c;最好先在掌握或起码了解dfs的基础上&#xff0c;再来阅读本文章&#xff0c;否则因为会有很多概念词看不明白…

Spring框架(下半部分 -AOP)

1.2 AOP相关的概念 1.2.1 AOP的概述 什么是AOP的技术&#xff1f; 在软件业&#xff0c;AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程 AOP是一种编程范式&#xff0c;隶属于软工范畴&#xff0c;指导开发者如何组织程序结构AOP最早由AO…

【开源-土拨鼠充电系统】鸿蒙 HarmonyOS 4.0 App+微信小程序+云平台

✨本人自己开发的开源项目&#xff1a;土拨鼠充电系统 ✨踩坑不易&#xff0c;还希望各位大佬支持一下&#xff0c;在Gitee或GitHub给我点个 Start ⭐⭐&#x1f44d;&#x1f44d; ✍Gitee开源项目地址&#x1f449;&#xff1a;https://gitee.com/cheinlu/groundhog-charging…

多维时序 | MATLAB实现BiTCN-selfAttention自注意力机制结合双向时间卷积神经网络多变量时间序列预测

多维时序 | MATLAB实现BiTCN-selfAttention自注意力机制结合双向时间卷积神经网络多变量时间序列预测 目录 多维时序 | MATLAB实现BiTCN-selfAttention自注意力机制结合双向时间卷积神经网络多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.M…

深度学习_20_卷积中的填充与步幅

如果图片本身比较小&#xff0c;卷积之后输出也会很小&#xff0c;那么可以在图片与卷积核相乘之前先填充一下&#xff0c;让输出为预期大小 一般填充后输入&#xff0c;输出相同 当图片比较大的时候&#xff0c;如果利用卷积核去得到我们想要的大小的话&#xff0c;得用到多层…

Linux虚拟主机如何快速卸载一键安装的网站程序

接到一位用户发送的请求帮助&#xff0c;想要卸载通过Softaculous一键安装的网站程序&#xff0c;但是没有找到地方&#xff0c;根据与该用户的沟通得知是一个小白用户&#xff0c;想要自己搭建一个博客类的网站&#xff0c;了解到Hostease 有适合新用户并且带管理面板的产品Li…

Nginx:部署及配置详解(linux)

Nginx&#xff1a;部署及配置详解&#xff08;linux&#xff09; 1、nginx简介2、安装编译工具及库文件3、安装 pcre4、nginx安装5、nginx配置文件nginx.conf组成6、nginx配置实例-反向代理7、nginx 配置实例-负载均衡 &#x1f496;The Begin&#x1f496;点点关注&#xff0c…