首页 > 编程语言 >DdddOcr 带带弟弟OCR通用验证码和 JAVA调用

DdddOcr 带带弟弟OCR通用验证码和 JAVA调用

时间:2024-05-23 20:58:39浏览次数:72  
标签:JAVA img DdddOcr det ddddocr result 带带 ocr type

本文主要参考 DdddOcr 发布的最新版本启动服务端, 以及JAVA 如何和服务端对接。
DdddOcr,其由作者与kerlomz共同合作完成,通过大批量生成随机数据后进行深度网络训练,本身并非针对任何一家验证码厂商而制作,本库使用效果完全靠玄学,可能可以识别,可能不能识别。

DdddOcr、最简依赖的理念,尽量减少用户的配置和使用成本,希望给每一位测试者带来舒适的体验

项目地址: 点我传送

赞助合作商

赞助合作商推荐理由
YesCaptcha谷歌reCaptcha验证码 / hCaptcha验证码 / funCaptcha验证码商业级识别接口 点我 直达VIP4
MaleniaMalenia企业级代理IP网关平台/代理IP分销软件

上手指南

在这里插入图片描述

安装步骤

i. 从pypi安装

pip install ddddocr

ii. 从源码安装

git clone https://github.com/sml2h3/ddddocr.git
cd ddddocr
python setup.py

请勿直接在ddddocr项目的根目录内直接import ddddocr,请确保你的开发项目目录名称不为ddddocr,此为基础常识。
文件目录说明
eg:

ddddocr 
├── MANIFEST.in
├── LICENSE
├── README.md
├── /ddddocr/
│  │── __init__.py            主代码库文件
│  │── common.onnx            新ocr模型
│  │── common_det.onnx        目标检测模型
│  │── common_old.onnx        老ocr模型
│  │── logo.png
│  │── README.md
│  │── requirements.txt
├── logo.png
└── setup.py

项目底层支持

本项目基于dddd_trainer 训练所得,训练底层框架位pytorch,ddddocr推理底层抵赖于onnxruntime,故本项目的最大兼容性与python版本支持主要取决于onnxruntime。
使用文档
i. 基础ocr识别能力
主要用于识别单行文字,即文字部分占据图片的主体部分,例如常见的英数验证码等,本项目可以对中文、英文(随机大小写or通过设置结果范围圈定大小写)、数字以及部分特殊字符。

# example.py
import ddddocr

ocr = ddddocr.DdddOcr()

image = open("example.jpg", "rb").read()
result = ocr.classification(image)
print(result)

本库内置有两套ocr模型,默认情况下不会自动切换,需要在初始化ddddocr的时候通过参数进行切换

# example.py
import ddddocr

ocr = ddddocr.DdddOcr(beta=True)  # 切换为第二套ocr模型

image = open("example.jpg", "rb").read()
result = ocr.classification(image)
print(result)

提示 对于部分透明黑色png格式图片得识别支持: classification 方法 使用 png_fix 参数,默认为False

 ocr.classification(image, png_fix=True)

注意

之前发现很多人喜欢在每次ocr识别的时候都重新初始化ddddocr,即每次都执行ocr = ddddocr.DdddOcr(),这是错误的,通常来说只需要初始化一次即可,因为每次初始化和初始化后的第一次识别速度都非常慢

参考例图

包括且不限于以下图片
在这里插入图片描述
ii. 目标检测能力

主要用于快速检测出图像中可能的目标主体位置,由于被检测出的目标不一定为文字,所以本功能仅提供目标的bbox位置 (在⽬标检测⾥,我们通常使⽤bbox(bounding box,缩写是 bbox)来描述⽬标位置。bbox是⼀个矩形框,可以由矩形左上⻆的 x 和 y 轴坐标与右下⻆的 x 和 y 轴坐标确定)

如果使用过程中无需调用ocr功能,可以在初始化时通过传参ocr=False关闭ocr功能,开启目标检测需要传入参数
det=True

import ddddocr
import cv2

det = ddddocr.DdddOcr(det=True)

with open("test.jpg", 'rb') as f:
    image = f.read()

bboxes = det.detection(image)
print(bboxes)

im = cv2.imread("test.jpg")

for bbox in bboxes:
    x1, y1, x2, y2 = bbox
    im = cv2.rectangle(im, (x1, y1), (x2, y2), color=(0, 0, 255), thickness=2)

cv2.imwrite("result.jpg", im)

参考例图

包括且不限于以下图片
在这里插入图片描述
在这里插入图片描述
Ⅲ. 滑块检测

本项目的滑块检测功能并非AI识别实现,均为opencv内置算法实现。可能对于截图党用户没那么友好~,如果使用过程中无需调用ocr功能或目标检测功能,可以在初始化时通过传参ocr=False关闭ocr功能或det=False来关闭目标检测功能

本功能内置两套算法实现,适用于两种不同情况,具体请参考以下说明

a.算法1

算法1原理是通过滑块图像的边缘在背景图中计算找到相对应的坑位,可以分别获取到滑块图和背景图,滑块图为透明背景图

滑块图
在这里插入图片描述

    det = ddddocr.DdddOcr(det=False, ocr=False)
    
    with open('target.png', 'rb') as f:
        target_bytes = f.read()
    
    with open('background.png', 'rb') as f:
        background_bytes = f.read()
    
    res = det.slide_match(target_bytes, background_bytes)
    
    print(res)

由于滑块图可能存在透明边框的问题,导致计算结果不一定准确,需要自行估算滑块图透明边框的宽度用于修正得出的bbox

提示:如果滑块无过多背景部分,则可以添加simple_target参数, 通常为jpg或者bmp格式的图片

    slide = ddddocr.DdddOcr(det=False, ocr=False)
    
    with open('target.jpg', 'rb') as f:
        target_bytes = f.read()
    
    with open('background.jpg', 'rb') as f:
        background_bytes = f.read()
    
    res = slide.slide_match(target_bytes, background_bytes, simple_target=True)
    
    print(res)

a.算法2

算法2是通过比较两张图的不同之处进行判断滑块目标坑位的位置

参考图a,带有目标坑位阴影的全图
在这里插入图片描述

在这里插入图片描述

    slide = ddddocr.DdddOcr(det=False, ocr=False)

    with open('bg.jpg', 'rb') as f:
        target_bytes = f.read()
    
    with open('fullpage.jpg', 'rb') as f:
        background_bytes = f.read()
    
    img = cv2.imread("bg.jpg")
    
    res = slide.slide_comparison(target_bytes, background_bytes)

    print(res)

Ⅳ. OCR概率输出

为了提供更灵活的ocr结果控制与范围限定,项目支持对ocr结果进行范围限定。

可以通过在调用classification方法的时候传参probability=True,此时classification方法将返回全字符表的概率 当然也可以通过set_ranges方法设置输出字符范围来限定返回的结果。

Ⅰ. set_ranges 方法限定返回字符返回

本方法接受1个参数,如果输入为int类型为内置的字符集限制,string类型则为自定义的字符集

如果为int类型,请参考下表
在这里插入图片描述
如果为string类型请传入一段不包含空格的文本,其中的每个字符均为一个待选词 如:“0123456789±x/=”"

import ddddocr

ocr = ddddocr.DdddOcr()

image = open("test.jpg", "rb").read()
ocr.set_ranges("0123456789+-x/=")
result = ocr.classification(image, probability=True)
s = ""
for i in result['probability']:
    s += result['charsets'][i.index(max(i))]

print(s)

本项目支持导入来自于 dddd_trainer 进行自定义训练后的模型,参考导入代码为

import ddddocr

ocr = ddddocr.DdddOcr(det=False, ocr=False, import_onnx_path="myproject_0.984375_139_13000_2022-02-26-15-34-13.onnx", charsets_path="charsets.json")

with open('test.jpg', 'rb') as f:
    image_bytes = f.read()

res = ocr.classification(image_bytes)
print(res)

服务端启动

安装上述文档安装后, 需要启动作为后台服务,这样可以更好的和JAVA 等异种语言的程序对接

start python /app.python/ocr_server.py --ocr --old

# encoding=utf-8
import argparse
import base64
import json

import ddddocr
from flask import Flask, request

parser = argparse.ArgumentParser(description="使用ddddocr搭建的最简api服务")
parser.add_argument("-p", "--port", type=int, default=9898)
parser.add_argument("--ocr", action="store_true", help="开启ocr识别")
parser.add_argument("--old", action="store_true", help="OCR是否启动旧模型")
parser.add_argument("--det", action="store_true", help="开启目标检测")

args = parser.parse_args()

app = Flask(__name__)


class Server(object):
    def __init__(self, ocr=True, det=False, old=False):
        self.ocr_option = ocr
        self.det_option = det
        self.old_option = old
        self.ocr = None
        self.det = None
        if self.ocr_option:
            print("ocr模块开启")
            if self.old_option:
                print("使用OCR旧模型启动")
                self.ocr = ddddocr.DdddOcr(old=True)
            else:
                print("使用OCR新模型启动,如需要使用旧模型,请额外添加参数  --old开启")
                self.ocr = ddddocr.DdddOcr()       
        elif self.det_option:
            print("det模块开启")
            self.det = ddddocr.DdddOcr(det=True)
        else:
            print("ocr or det 模块未开启!")

    def classification(self, img: bytes):
        if self.ocr_option:
            return self.ocr.classification(img)
        else:
            raise Exception("ocr模块未开启")

    def detection(self, img: bytes):
        if self.det_option:
            return self.det.detection(img)
        else:
            raise Exception("目标检测模块模块未开启")

    def slide(self, target_img: bytes, bg_img: bytes, algo_type: str):
        dddd = self.ocr or self.det or ddddocr.DdddOcr(ocr=False)
        if algo_type == 'match':
            return dddd.slide_match(target_img, bg_img)
        elif algo_type == 'compare':
            return dddd.slide_comparison(target_img, bg_img)
        else:
            raise Exception(f"不支持的滑块算法类型: {algo_type}")

server = Server(ocr=args.ocr, det=args.det, old=args.old)


def get_img(request, img_type='file', img_name='image'):
    if img_type == 'b64':
        img = base64.b64decode(request.get_data()) # 
        try: # json str of multiple images
            dic = json.loads(img)
            img = base64.b64decode(dic.get(img_name).encode())
        except Exception as e: # just base64 of single image
            pass
    else:
        img = request.files.get(img_name).read()
    return img


def set_ret(result, ret_type='text'):
    if ret_type == 'json':
        if isinstance(result, Exception):
            return json.dumps({"status": 200, "result": "", "msg": str(result)})
        else:
            return json.dumps({"status": 200, "result": result, "msg": ""})
        # return json.dumps({"succ": isinstance(result, str), "result": str(result)})
    else:
        if isinstance(result, Exception):
            return ''
        else:
            return str(result).strip()


@app.route('/<opt>/<img_type>', methods=['POST'])
@app.route('/<opt>/<img_type>/<ret_type>', methods=['POST'])
def ocr(opt, img_type='file', ret_type='text'):
    try:
        print('opt='+opt+',img_type='+img_type)
        img = get_img(request, img_type)
        if opt == 'ocr':
            result = server.classification(img)
            print('ocr result='+result)
        elif opt == 'det':
            result = server.detection(img)
            print('det result='+result)
        else:
            raise f"<opt={opt}> is invalid"
        return set_ret(result, ret_type)
    except Exception as e:
        return set_ret(e, ret_type)

@app.route('/slide/<algo_type>/<img_type>', methods=['POST'])
@app.route('/slide/<algo_type>/<img_type>/<ret_type>', methods=['POST'])
def slide(algo_type='compare', img_type='file', ret_type='text'):
    try:
        target_img = get_img(request, img_type, 'target_img')
        bg_img = get_img(request, img_type, 'bg_img')
        result = server.slide(target_img, bg_img, algo_type)
        return set_ret(result, ret_type)
    except Exception as e:
        return set_ret(e, ret_type)

@app.route('/ping', methods=['GET'])
def ping():
    return "pong"


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=args.port)

JAVA 客户端

public String getImgCode(byte[] bigImage) {
		try {
			if (ddddUrl == null) {
				System.out.println("ddddUrl=" + ddddUrl);
				return null;
			}

			long time = (new Date()).getTime();
			HttpURLConnection con = null;
			String boundary = "----------" + String.valueOf(time);
			String boundarybytesString = "\r\n--" + boundary + "\r\n";
			OutputStream out = null;

			URL u = new URL(ddddUrl);

			con = (HttpURLConnection) u.openConnection();
			con.setRequestMethod("POST");
			con.setConnectTimeout(10000);
			con.setReadTimeout(10000);
			con.setDoOutput(true);
			con.setDoInput(true);
			con.setUseCaches(true);
			con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

			out = con.getOutputStream();

			if (bigImage != null && bigImage.length > 0) {
				out.write(boundarybytesString.getBytes("UTF-8"));
				String paramString = "Content-Disposition: form-data; name=\"image\"; filename=\"" + "bigNxt.gif" + "\"\r\n";
				paramString += "Content-Type: application/octet-stream\r\n\r\n";
				out.write(paramString.getBytes("UTF-8"));
				out.write(bigImage);
			}

			String tailer = "\r\n--" + boundary + "--\r\n";
			out.write(tailer.getBytes("UTF-8"));

			out.flush();
			out.close();

			StringBuffer buffer = new StringBuffer();
			BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
			String temp;
			while ((temp = br.readLine()) != null) {
				buffer.append(temp);
			}
			String ret = buffer.toString();
			if (ret.length() < 1) {
				System.out.println("ddddUrl=" + ddddUrl + " ret=" + buffer.toString());
			}
			return buffer.toString();
		} catch (Throwable e) {
			logger.error("ddddUrl=" + ddddUrl + ",e=" + e.toString());
			return null;
		}
	}

标签:JAVA,img,DdddOcr,det,ddddocr,result,带带,ocr,type
From: https://blog.csdn.net/weixin_46641057/article/details/139157514

相关文章

  • [Java]反射
    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)https://www.cnblogs.com/cnb-yuchen/p/17960654出自【进步*于辰的博客】参考笔记二,P75.3;笔记三,P15.2、P43.2、P44.2、P64.3、P69.1。1、什么是“反射”?关于类加载,详述可查阅博文《[Java]知识点》中的【类加载......
  • Java.年月日正则表达式
    表达式:yyyy-MM:^([1-9]{1}[0-9]{3}[\\-]{1}){1}((1[0-2]{1}){1}|(0[1-9]{1})|([1-9]{1})){1}$yyyy-MM-dd:^((((19|20)\\d{2})(-)(0?[13578]|1[02])-(0?[1-9]|[12]\\d|3[01]))|(((19|20)\\d{2})(-)(0?[469]|11)-(0?[1-9]|[12]\\d|30))|(((19|20)\\d{2})(-)(0......
  • JavaScript中reduce()详解及使用方法。
    一、定义和用法reduce()方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。reduce()可以作为一个高阶函数,用于函数的compose。reduce()方法为归并类方法,最常用的场景就是,计算数组中的每一项的总和。注意:reduce()对于空数组是不会执行回调函数的......
  • Spring AI 抢先体验,5 分钟玩转 Java AI 应用开发
    SpringAI是Spring官方社区项目,旨在简化JavaAI应用程序开发,让Java开发者像使用Spring开发普通应用一样开发AI应用。SpringCloudAlibabaAI以SpringAI为基础,并在此基础上提供阿里云通义系列大模型全面适配,让用户在5分钟内开发基于通义大模型的JavaAI应用。......
  • java 通过 microsoft graph 调用outlook(三)
    这次会添加一个Reply接口,并且使用6.10.0版本 直接上代码一,POM<!--office365--><dependency><groupId>com.microsoft.graph</groupId><artifactId>microsoft-graph</artifactId><......
  • Pairwise实现(Java篇)
    importjava.util.HashMap;/***PairWise(成对)测试方法*author:likeqc*date:2021-4-411:06:59*/classPairWise{/***@paramstrString[][],二维数组,一维数组str[i]中存放第i个因素的因子*/privatestaticvoidsolution(String[][]s......
  • 位运算符在 Javascript 中的运用
    零、资料JavaScript中的位运算和权限设计javascript位运算技巧巧用JS位运算JavaScript位运算及其妙用聊聊JavaScript中的二进制数一、权限在权限设计时,每一个基础权限单元都是二进制数形式,有且只有一位值是1,其余全部是0,即权限码是 2^n 。所以,在这套设......
  • 关于如何使用JNI将C语言接口打包成可供java环境调用的so库文件
    一、环境检查在linux下打包.so文件,首先需要确认是否有安装java环境,可通过在终端中输入指令java的方式来进行查看。如下图所示,则为已安装java环境。  若当前未安装java环境,则可通过在终端中输入如下指令进行安装,我这里使用的java环境为1.8.0版本。sudoapt-getinstallo......
  • 使用-HTML5-和-JavaScript-开发-Windows-商店应用-全-
    使用HTML5和JavaScript开发Windows商店应用(全)原文:zh.annas-archive.org/md5/8F13EC8AC7BDB8535E7218C5DDB48475译者:飞龙协议:CCBY-NC-SA4.0序言使用HTML5和JavaScript开发WindowsStore应用是一本实践性强的指南,涵盖了WindowsStore应用的基本重要特性以及......
  • 精通-JavaScript-高性能-全-
    精通JavaScript高性能(全)原文:zh.annas-archive.org/md5/582AFDEF15013377BB79AB8CEA3B2B47译者:飞龙协议:CCBY-NC-SA4.0序言欢迎来到精通JavaScript高性能。在这本书中,我们已经以帮助任何JavaScript开发者,无论他们是新手上路还是经验丰富的老手的方式,覆盖了JavaScrip......