C# 实现电子签名

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

直接看效果图

1.主页面

C# 实现电子签名_第1张图片

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

C# 实现电子签名_第2张图片

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

C# 实现电子签名_第3张图片

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

C# 实现电子签名_第4张图片

放大看

 C# 实现电子签名_第5张图片

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

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

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

C# 实现电子签名_第6张图片

双击右边第一张放大:

C# 实现电子签名_第7张图片

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

4.算法介绍:

4.1自动适配

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

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

            imgShow = new Image(new Bitmap(pictureBox1.Image));
            gray = imgShow.Convert();

            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:

        /// 
        /// Canny生成
        /// 
        /// 
        /// 
        private void button6_Click(object sender, EventArgs e)
        {
            if (gray == null || imgShow == null)
            {
                MessageBox.Show("请先选择自动适配");
                return;
            }
            // Canny算子边缘检测
            using Image 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 mask = new Image(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 maskedImage = new Image(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,即为透明。

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

            // 掩码
            using Image 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方法,多迭代几次就可以,不过效率低

C# 实现电子签名_第8张图片

C# 实现电子签名_第9张图片 

 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();
        }
       

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

你可能感兴趣的:(c#,开发语言)