IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> python实现签章抠图,背景透明。签字、印章抠图,便于线上使用 -> 正文阅读

[人工智能]python实现签章抠图,背景透明。签字、印章抠图,便于线上使用

线上审批等场景经常会用到手写签名、公司鲜章等,这篇文章介绍的就是如何定位抠图A4纸上的签名和鲜章的,并且可以批量处理。

主要使用opencv进行图像处理,把图像中的文字和印章轮廓处理出来,然后再进行定位裁剪,最后背景透明化。

先放效果图

扫描原图

抠出的印章在表格上的效果

自动定位图片上的所有签字并抠图

抠出签名的效果

代码

?印章部分

def yz(imgname):
    original_image = cv2.imread(imgname)
    image = original_image.copy()
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (3, 3), 0)
    canny = cv2.Canny(blurred, 120, 255, 1)

    # 找到图片中的轮廓
    cnts = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    # 按照面积将所有轮廓逆序排序
    contours2 = sorted(cnts, key=lambda a: cv2.contourArea(a), reverse=True)
    ROI_number = 0
    for c in contours2:
        area = cv2.contourArea(c)
        print(area)
        # 只抠面积大于1200的轮廓,一般印章的轮廓面积比较大,可根据实际情况调整
        if area < 1200: break
        x, y, w, h = cv2.boundingRect(c)
        # 调整裁剪的位置,避免印章的边缘缺失
        x -= 20
        y -= 20
        w += 40
        h += 40
        cv2.rectangle(image, (x, y), (x + w, y + h), (36, 255, 12), 1)
        ROI = original_image[y:y + h, x:x + w]
        # cv2.imshow("ROI", ROI)
        img_name = imgname[0: imgname.rindex(".")] + '_{}.png'.format(ROI_number)
        cv2.imwrite(img_name, ROI)
        pic = Image.open(img_name)
        # 转为RGBA模式
        pic = pic.convert('RGBA')
        width, height = pic.size
        # 获取图片像素操作入口
        array = pic.load() 
        for i in range(width):
            for j in range(height):
                # 获得某个像素点,格式为(R, G, B, A)元组
                pos = array[i, j]
                # RGB三者都大于240(很接近白色了)
                isEdit = (sum([1 for x in pos[0:3] if x > 240]) == 3)
                if isEdit:
                    # 更改为透明
                    array[i, j] = (255, 255, 255, 0)

        # 保存图片
        pic.save(img_name)
        ROI_number += 1

?签字部分,原理和印章一样,只不过签字有的不是连着的,所以要合并相邻的轮廓,保证扣出来的是完整的签名

def opcvimg(cur_path):
    # 加载图像、灰度、高斯模糊、自适应阈值
    image = cv2.imread(cur_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (9, 9), 0)
    thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 30)

    # 扩大以合并相邻的文本轮廓
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
    dilate = cv2.dilate(thresh, kernel, iterations=6)

    # 查找轮廓、突出显示文本区域并提取 ROI
    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    ROI_number = 0
    for c in cnts:
        area = cv2.contourArea(c)
        if area > 100:
            x, y, w, h = cv2.boundingRect(c)
            x -= 20
            y -= 20
            w += 40
            h += 40
            # cv2.rectangle(image, (x, y), (x + w, y + h), (36, 255, 12), 2)
            ROI = image[y:y + h, x:x + w]
            img_name = cur_path[0: cur_path.rindex(".")] + '_{}.png'.format(ROI_number)
            cv2.imwrite(img_name, ROI)
            ROI_number += 1
            cv2.imwrite(img_name, ROI)
            pic = Image.open(img_name)
            pic = pic.convert('RGBA') 
            width, height = pic.size
            array = pic.load()
            for i in range(width):
                for j in range(height):
                    pos = array[i, j]
                    isEdit = (sum([1 for x in pos[0:3] if x > 240]) == 3)
                    if isEdit:
                        array[i, j] = (255, 255, 255, 0)

            pic.save(img_name)

完整代码,引入flask-restful,api使得程序可以组件化单独部署,提供抠图能力

#!/usr/bin/python
# encoding: utf-8

import cv2
import PIL.Image as Image
import os
from flask import Flask
from flask_restful import Api, Resource, request

app = Flask(__name__)
api = Api(app)


class DetectSign(Resource):
    """ 提取签名和印章 """

    def get(self):
        try:
            path = request.args['path']
            type = request.args['type']
            show_files(path, type)
            return {"msg": "成功", "code": 200}
        except Exception as e:
            print(e)

        return {"msg": "失败", "code": 500}


def show_files(path, type):
    if path is None:
        return
    if ".jpg" in path or ".png" in path or ".gif" in path:
        try:
            if type == '0':
                yz(path)
            else:
                opcvimg(path)
        except Exception as e:
            print(e)
    else:
        # 首先遍历当前目录所有文件及文件夹
        file_list = os.listdir(path)
        # 准备循环判断每个元素是否是文件夹还是文件,是文件的话,把名称传入list,是文件夹的话,递归
        for file in file_list:
            # 利用os.path.join()方法取得路径全名,并存入cur_path变量,否则每次只能遍历一层目录
            cur_path = os.path.join(path, file)
            # 判断是否是文件夹
            if os.path.isdir(cur_path):
                show_files(cur_path)
            else:
                print(cur_path)
                if ".jpg" in cur_path or ".png" in cur_path or ".gif" in cur_path:
                    try:
                        if type == '0':
                            yz(cur_path)
                        else:
                            opcvimg(cur_path)
                    except Exception as e:
                        print(e)


def opcvimg(cur_path):
    image = cv2.imread(cur_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (9, 9), 0)
    thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 30)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
    dilate = cv2.dilate(thresh, kernel, iterations=6)

    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    ROI_number = 0
    for c in cnts:
        area = cv2.contourArea(c)
        if area > 100:
            x, y, w, h = cv2.boundingRect(c)
            x -= 20
            y -= 20
            w += 40
            h += 40
            # cv2.rectangle(image, (x, y), (x + w, y + h), (36, 255, 12), 2)
            ROI = image[y:y + h, x:x + w]
            img_name = cur_path[0: cur_path.rindex(".")] + '_{}.png'.format(ROI_number)
            cv2.imwrite(img_name, ROI)
            ROI_number += 1
            cv2.imwrite(img_name, ROI)
            pic = Image.open(img_name)
            pic = pic.convert('RGBA')
            width, height = pic.size
            array = pic.load()
            for i in range(width):
                for j in range(height):
                    pos = array[i, j
                    isEdit = (sum([1 for x in pos[0:3] if x > 240]) == 3)
                    if isEdit:
                        array[i, j] = (255, 255, 255, 0)

            pic.save(img_name)


def yz(imgname):
    original_image = cv2.imread(imgname)
    image = original_image.copy()
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (3, 3), 0)
    canny = cv2.Canny(blurred, 120, 255, 1)
    cnts = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    contours2 = sorted(cnts, key=lambda a: cv2.contourArea(a), reverse=True)
    ROI_number = 0
    for c in contours2:
        area = cv2.contourArea(c)
        print('图像面积:', area)
        if area < 1200: break
        x, y, w, h = cv2.boundingRect(c)
        x -= 20
        y -= 20
        w += 40
        h += 40
        cv2.rectangle(image, (x, y), (x + w, y + h), (36, 255, 12), 1)
        ROI = original_image[y:y + h, x:x + w]
        # cv2.imshow("ROI", ROI)
        img_name = imgname[0: imgname.rindex(".")] + '_{}.png'.format(ROI_number)
        cv2.imwrite(img_name, ROI)
        pic = Image.open(img_name)
        pic = pic.convert('RGBA')
        width, height = pic.size
        array = pic.load()
        for i in range(width):
            for j in range(height):
                pos = array[i, j]
                isEdit = (sum([1 for x in pos[0:3] if x > 240]) == 3)
                if isEdit:
                    array[i, j] = (255, 255, 255, 0)

        pic.save(img_name)
        ROI_number += 1


# 设置路由
api.add_resource(DetectSign, '/x-api/v1/ai/detect/sign')

if __name__ == '__main__':
    # 将host设置为0.0.0.0,则外网用户也可以访问到这个服务
    app.run('0.0.0.0', 8386, debug=True)

有编译打包好的 exe程序文件可以下载。

https://download.csdn.net/download/puremilkll/21739417

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-08-31 15:27:21  更:2021-08-31 15:29:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 16:45:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码