C# 实现电子签名

news2024/11/26 16:44:37

本项目基于Emgu.CV(C#下OpenCv的封装)开发的,编译器最新版Vs2022,编译环境x86

直接看效果图

1.主页面

2.我们先看手写的方式:

点击确认就到主界面,如下 :

点击自动适配-,再点击生成:

放大看

 

点击保存即可,生成透明电子签名图片。

3.在单色背景下手写名字,导入图片生成

先点击 选择图像 按钮,然后选择图像,点击自动适配,点击Canny算法生成,最后点击生成,如下:

双击右边第一张放大:

选择需要的区域点击保存即可。

4.算法介绍:

4.1自动适配

自动适配指根据灰度值像素选取Canny算子的上下阈值,主要目的是能快速自动适配Canny上下阈值,这里主要介绍以中位数或者平均值,然后以Sigma为临界值选取一个区间,如下:

        /// <summary>
        /// 自动适配
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button4_Click(object sender, EventArgs e)
        {
            if (pictureBox1.Image == null)
            {
                MessageBox.Show("请选择图像");
                return;
            }
            imgShow?.Dispose();
            gray?.Dispose();

            imgShow = new Image<Bgra, byte>(new Bitmap(pictureBox1.Image));
            gray = imgShow.Convert<Gray, byte>();

            double median = radioButton1.Checked ? CalculateMedian(gray) : CalculateAvg(gray);

            double sigma = (float)numericUpDown1.Value;
            double lower = Math.Max(0, (1.0 - sigma) * median);
            double upper = Math.Min(255, (1.0 + sigma) * median);
            trackBar1.Value = (int)lower;
            label1.Text = trackBar1.Value.ToString();
            trackBar2.Value = (int)upper;
            label3.Text = trackBar2.Value.ToString();
        }

 4.2Canny生成

Canny算子边缘检测具体不多介绍,这里目的是为了找到字体边缘,然后在图像上填充,方便我们能确定这个边缘是否能够准确找到,如果大都填充到字体上,说明边缘检测不错,找到这些边缘,然后计算它们的平均R、G、B:

        /// <summary>
        /// Canny生成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button6_Click(object sender, EventArgs e)
        {
            if (gray == null || imgShow == null)
            {
                MessageBox.Show("请先选择自动适配");
                return;
            }
            // Canny算子边缘检测
            using Image<Gray, byte> edges = gray.Canny(trackBar1.Value, trackBar2.Value);
            using VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            using Mat hier = new Mat();

            CvInvoke.FindContours(edges, contours, hier, RetrType.List, ChainApproxMethod.ChainApproxSimple);
            // 掩码
            using Image<Gray, byte> mask = new Image<Gray, byte>(gray.Size);
            if (contours.Size > 10000)
            {
                MessageBox.Show("边缘太多,会非常耗时,请重先调节参数");
                return;
            }
            // 在imgShow图像上填充边缘边框
            for (int i = 0; i < contours.Size; i++)
            {
                var contour = contours[i];
                CvInvoke.DrawContours(imgShow, contours, i, new MCvScalar(0, 0, 255), 2);
                CvInvoke.DrawContours(mask, contours, i, new MCvScalar(255), thickness: -1);
            }
            this.pictureBox3.Image?.Dispose();
            this.pictureBox3.Image = imgShow.Bitmap;

            using Image<Gray, byte> maskedImage = new Image<Gray, byte>(gray.Size);
            CvInvoke.BitwiseAnd(gray, gray, maskedImage, mask);
            MCvScalar average = CvInvoke.Mean(maskedImage, mask);
            double blue = average.V0;
            double green = average.V1;
            double red = average.V2;
            trackBar3.Value = (int)Math.Round(red, 0);
            trackBar4.Value = (int)Math.Round(green, 0);
            trackBar5.Value = (int)Math.Round(blue, 0);
            label9.Text = trackBar3.Value.ToString();
            label10.Text = trackBar4.Value.ToString();
            label12.Text = trackBar5.Value.ToString();
        }

4.3生成

在生成中,我们会根据R、G、B计算出掩码,然后将掩码中选取的颜色的alpha通道设置为0,即为透明。

        /// <summary>
        /// 生成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            if (pictureBox1.Image == null)
            {
                MessageBox.Show("请选择图片");
                return;
            }
            img?.Dispose();
            img = new Image<Bgra, byte>(new Bitmap(pictureBox1.Image));

            // 掩码
            using Image<Gray, byte> mask2 = img.InRange(new Bgra(trackBar5.Value, trackBar4.Value, trackBar3.Value, 0), new Bgra(255, 255, 255, 255));
            // 将掩码中选取的颜色的alpha通道设置为0
            img.SetValue(new Bgra(0, 0, 0, 0), mask2);


            this.pictureBox2.Image?.Dispose();
            this.pictureBox2.Image = img.Bitmap;
        }

5.也可以直接调用GrabCut方法,多迭代几次就可以,不过效果并不是很好,且效率低

 private void button2_Click(object sender, EventArgs e)
        {
            if(this.pictureBox1.Image==null)
            {
                return;
            }
            float scaleX = (float)this.pictureBox1.Image.Width / pictureBox1.Width;
            float scaleY = (float)this.pictureBox1.Image.Height / pictureBox1.Height;

            Rectangle rect = new Rectangle(
                (int)(Rect.Left * scaleX),
                (int)(Rect.Top * scaleY),
                (int)(Rect.Width * scaleX),
                (int)(Rect.Height * scaleY));
            var rectangle = new OpenCvSharp.Rect(rect.X, rect.Y, rect.Width, rect.Height);

            rect.Intersect(new Rectangle(0, 0, this.pictureBox1.Image.Width, this.pictureBox1.Image.Height));

            using Mat nimage = new Bitmap(this.pictureBox1.Image).ToMat();
            using Mat image = new Mat(nimage, rectangle);
            using Mat convertedImage = new Mat();
            Cv2.CvtColor(image, convertedImage, ColorConversionCodes.BGRA2BGR); // 将图像转换为CV_8UC3类型
           

            using Mat mask = new Mat(convertedImage.Size(), MatType.CV_8UC1, Scalar.Black);
            using Mat bgdModel = new Mat();
            using Mat fgdModel = new Mat();

            var r = new OpenCvSharp.Rect(0, 0, rectangle.Width-10, rectangle.Height-10);
            Cv2.GrabCut(convertedImage, mask, r, bgdModel, fgdModel, 20, GrabCutModes.InitWithRect);
            Cv2.Threshold(mask, mask, 2, 255, ThresholdTypes.Binary);
            using Mat foreground = new Mat();
            Cv2.BitwiseAnd(convertedImage, convertedImage, foreground, mask);


            // 创建一个具有 alpha 通道的新图像
            using Mat result = new Mat();
            Cv2.CvtColor(image, result, ColorConversionCodes.BGRA2BGR);
            Cv2.Rectangle(result, rectangle, new Scalar(0, 0, 0), 1); // 绘制矩形边界
            Cv2.Multiply(result, new Scalar(1, 1, 1, 0), result); // 将背景部分置为透明

            // 将前景部分复制到结果图像中
            image.CopyTo(result, mask);

            pictureBox2.Image?.Dispose();
            pictureBox2.Image = result.ToBitmap();
            image.Dispose();
        }
       

 

 

注:如果需要其它源码,请留言邮箱,我看到私发。

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

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

相关文章

windows安装向量数据库milvus

本文介绍windows下安装milvus的方法。 一.Docker安装 1.1docker下载 首先到Docker官网上下载docker:Docker中文网 官网 1.2.安装前前期准备 先使用管理员权限打开windows powershell 然后在powershell里面输入下面那命令&#xff0c;启用“适用于 Linux 的 Windows 子系统”…

JMeter压力测试入门教程

Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于Web应用测试但后来扩展到其他测试领域。 它可以用于测试静态和动态资源例如静态文件、Java小服务程序、CGI脚本、Java 对象、数据库&#xff0c; FTP服务器, 等等。J…

JavaScript中的深拷贝和浅拷贝

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a;⭐深拷贝&#xff08;Deep Copy&#xff09;&#xff1a;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带…

数据结构day7栈-顺序栈的实现

用指针比用数组好&#xff0c;这样用户可以自己指定空间的大小&#xff0c;有参与感。 全部代码: main.c #include <stdio.h> #include <string.h> #include "sqstack.h"int main(int argc, char *argv[]) {sqstack *s;int i;s stack_create(100);if(…

数据结构 - 单链表

文章目录 目录 文章目录 一、什么是链表? 线性表: 顺序表: 二、链表的分类和实现 分类: 实现: 1.创建节点类 2.创建单链表 1.addTail(尾增) 2.删除节点值为key的第一个节点 3.插入节点(在指定位置) 4.获取链表长度 总结 前言 大家好,这篇博客给大家讲一下什么是…

《代码随想录》刷题笔记——数组篇【java实现】

*二分查找 题目链接 https://leetcode.cn/problems/binary-search/ 左闭右闭区间实现 时间复杂度&#xff1a;O(log n)空间复杂度&#xff1a;O(1) /*** 左闭右闭写法** param nums* param target* return*/ public static int search1(int[] nums, int target) {if (nums…

关于工信部发布的app备案以及小程序备案流程

一、相关政策 通知&#xff1a;https://beian.miit.gov.cn/#/Integrated/lawStatute 腾讯备案&#xff1a;网站备案 首次备案-网站备案-文档中心-腾讯云 阿里备案&#xff1a;网站备案_ICP备案_备案迁移_备案-阿里云 二、遇到的问题 APP备案 安卓获取平台公钥方法&#xf…

Vue + Element UI 前端篇(九):接口格式定义

接口请求格式定义 前台显示需要后台数据&#xff0c;我们这里先把前后端交互接口定义好&#xff0c;没有后台的时候&#xff0c;也方便用mock模拟。 接口定义遵循几个规范&#xff1a; 1. 接口按功能模块划分。 系统登录&#xff1a;登录相关接口 用户管理&#xff1a;用户…

Redis-1.4-过期策略

1 设置带过期时间的 key # 时间复杂度&#xff1a;O&#xff08;1&#xff09;&#xff0c;最常用方式 expire key seconds# 字符串独有方式 setex(String key, int seconds, String value)除了string独有设置过期时间的方法&#xff0c;其他类型都需依靠expire方法设置时间&a…

说说MySQL回表查询与覆盖索引

分析&回答 什么是回表查询&#xff1f; 通俗的讲就是&#xff0c;如果索引的列在 select 所需获得的列中&#xff08;因为在 mysql 中索引是根据索引列的值进行排序的&#xff0c;所以索引节点中存在该列中的部分值&#xff09;或者根据一次索引查询就能获得记录就不需要…

【Linux】Qt Remote之Remote开发环境搭建填坑小记

总体思路 基于WSL2&#xff08;Ubuntu 22.04 LTS&#xff09;原子Alpha开发板进行Qt开发实验&#xff0c;基于Win11通过vscode remote到WSL2&#xff0c;再基于WSL2通过Qt 交叉编译&#xff0c;并通过sshrsync远程到开发板&#xff0c;构建起开发工具链。 Step1 基于Win11通过…

Mock接口测试

什么是mock? 测试桩&#xff0c;模拟被测对象的返回&#xff0c;用于测试 通常意义的mock指的就是mock server, 模拟服务端返回的接口数据&#xff0c;用于前端开发&#xff0c;第三方接口联调 为什么要mock? 1. 解决依赖问题&#xff1a;当我们测试一个接口或者功能模块…

Python Opencv实践 - 轮廓特征(最小外接圆,椭圆拟合)

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/stars.PNG") plt.imshow(img[:,:,::-1])#轮廓检测 img_gray cv.cvtColor(img, cv.COLOR_BGR2GRAY) ret,thresh cv.threshold(img_gray, 127, 255, 0) contou…

Unity汉化一个插件 制作插件汉化工具

我是编程一个菜鸟&#xff0c;英语又不好&#xff0c;有的插件非常牛&#xff01;我想学一学&#xff0c;页面全是英文&#xff0c;完全不知所措&#xff0c;我该怎么办啊...尝试在Unity中汉化一个插件 效果&#xff1a; 思路&#xff1a; 如何在Unity中把一个自己喜欢的插件…

Flutter 最优秀动画库「完全商业化」,Rive 2 你全面了解过吗?

说到 rive &#xff0c;非 Flutter 开发者可能会感觉比较陌生&#xff0c;而做过 Flutter 开发的可能对 rive 会有所耳闻&#xff0c;因为 rive 在一开始叫 flare &#xff0c;是 2dimensions 公司的开源动画产品&#xff0c;在发布之初由于和 Flutter 团队有深入合作&#xff…

golang获取prometheus数据(prometheus/client_golang包)

文章目录 1. 创建链接1.1 语法1.2 完整示例 2. 简单查询2.1 语法2.2 完整示例 3. 范围值查询3.1 语法3.2 完整示例 【附官方示例】 1. 创建链接 1.1 语法 语法 func NewClient(cfg Config) (Client, error)结构体 type Config struct {Address stringClient *ht…

16-数据结构-图的存储结构

简介&#xff1a;主要为图的顺序存储和链式存储。其中顺序存储即邻接矩阵的画法以及代码&#xff0c;邻接矩阵又分为有权图和无权图&#xff0c;区别就是有数据的地方填权值&#xff0c;无数据的地方可以填0或者∞&#xff0c;而有权图和无权图&#xff0c;又细分为有向图和无向…

阿里云服务器退款政策及退款流程解析

阿里云服务器如何退款&#xff1f;云服务器在哪申请退款&#xff1f;在用户中心订单管理中的退订管理中退款&#xff0c;阿里云百科分享阿里云服务器退款流程&#xff0c;包括申请退款入口、云服务器退款限制条件、退款多久到账等详细说明&#xff1a; 目录 阿里云服务器退款…

FPGA GTH 全网最细讲解,aurora 8b/10b协议,HDMI板对板视频传输,提供2套工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、GTH 全网最细解读GTH 基本结构GTH 发送和接收处理流程GTH 的参考时钟GTH 发送接口GTH 接收接口GTH IP核调用和使用 4、设计思路框架视频源选择silicon9011解码芯片配置及采集动态彩条视频数据组包GTH aurora 8b/10…

【Java 基础篇】深入理解 Java 内部类:嵌套在嵌套中的编程奇妙世界

在 Java 编程中&#xff0c;内部类&#xff08;Inner Class&#xff09;是一个非常强大且灵活的概念&#xff0c;它允许在一个类的内部定义另一个类。内部类可以访问外部类的成员&#xff0c;包括私有成员&#xff0c;这使得内部类在许多编程场景中都非常有用。本篇博客将详细介…