本项目基于Emgu.CV(C#下OpenCv的封装)开发的,编译器最新版Vs2022,编译环境x86
直接看效果图
1.主页面
2.我们先看手写的方式:
点击确认就到主界面,如下 :
点击自动适配-,再点击生成:
放大看
点击保存即可,生成透明电子签名图片。
3.在单色背景下手写名字,导入图片生成
先点击 选择图像 按钮,然后选择图像,点击自动适配,点击Canny算法生成,最后点击生成,如下:
双击右边第一张放大:
选择需要的区域点击保存即可。
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
byte>(new Bitmap(pictureBox1.Image)); - gray = imgShow.Convert
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:
- ///
- /// Canny生成
- ///
- ///
- ///
- private void button6_Click(object sender, EventArgs e)
- {
- if (gray == null || imgShow == null)
- {
- MessageBox.Show("请先选择自动适配");
- return;
- }
- // Canny算子边缘检测
- using Image
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
byte> mask = new Imagebyte>(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
byte> maskedImage = new Imagebyte>(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
byte>(new Bitmap(pictureBox1.Image)); -
- // 掩码
- using Image
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();
- }
注:如果需要其它源码,请留言邮箱,我看到私发。