windows c++&&c# 生成数字+大写字母的36个字符训练集

字符训练集生成源码

由于项目需要要完成OCR 字符识别,想要使用深度学习来训练字符分类,根据环境需求要完成多种字体,变形,缺失,字符颜色随机的情况下识别字符,所以想要自己生成符合需求的字数训练数据集。

1.筛选需要的字体种类,生成各种字体的各个字符的黑色背景白色字体的图片

使用查询c# Winform 查看windows 系统自带的字体,总共108中,谁选需要的字体(很多字体存在异形符号,同时考虑训练数据集的大小问题,筛除相似的字体)
遍历生成36个文件,和各种字体的字符。
示列代码:

  using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace getChar36
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        int imgsize = 100;
        int fontsize = 24;
        string dirp = "Charimage";
        private void button1_Click(object sender, EventArgs e)
        {
            //int[] nook = { 18, 19, 20, 21, 22, 23, 24, 28, 31, 33, 41, 56, 57, 62, 63, 74, 75, 76, 77, 78, 79, 64, 86, 81, 87 };
            int[] nook = { 18, 19, 20, 21, 22, 23, 24, 28, 31, 33, 41, 56, 57, 62, 63,72, 74, 75,
                76, 77, 78, 79, 64, 86, 81, 87,1,14,15,16,17,25,27,30,34,37,42,45,46,48,58,61,73,
                80,82,85,88,89,90,95,97,101,102,104,105,106,107,32,66,26,29,52,53,54,55,83,96,94,100,60,84,103};不符合字体种类的id
            int[] ok = { 0, 2, 3, 4, 6, 7, 51, 40, 36 };
            for (int k = 0; k <= 9; k++)
            {

                string subPath = "D://" + dirp + "//" + k.ToString() + "//";
                System.IO.Directory.CreateDirectory(subPath);

                StringBuilder str = new StringBuilder(2000);
                System.Drawing.Text.InstalledFontCollection fonts = new System.Drawing.Text.InstalledFontCollection();
                int i = 0;
                #region
                foreach (System.Drawing.FontFamily family in fonts.Families)
                {
                    bool isok = true;
                    for (int j = 0; j < nook.Length; j++)
                    {
                        if (nook[j] == i)
                        {
                            isok = false;
                        }
                    }

                    if (isok)
                    {
                        Rectangle rect = new Rectangle(0, 0, imgsize, imgsize);
                        Graphics dg = this.CreateGraphics();
                        Image image = new Bitmap(rect.Width, rect.Height, dg);
                        Graphics ig = Graphics.FromImage(image);
                        ig.FillRectangle(Brushes.Black, rect);
                        Font font = new Font(family.Name, fontsize);
                        Brush brush = System.Drawing.Brushes.White;
                        ig.DrawString(k.ToString(), font, brush, 15, 15);
                        // ImageFormat iformat = new ImageFormat(new Guid(Bmp));
                        image.Save("D://" + dirp + "//" + k.ToString() + "//" + k.ToString() + "_" + i.ToString() + ".jpg");
                        dg.DrawImage(image, new PointF(imgsize, imgsize));

                    }
                    i++;
                }
                #endregion
            }

            for (int k = 65; k <= 90; k++)
            {

                string subPath = "D://" + dirp + "//" + (k - 55).ToString() + "//";
                System.IO.Directory.CreateDirectory(subPath);

                StringBuilder str = new StringBuilder(2000);
                System.Drawing.Text.InstalledFontCollection fonts = new System.Drawing.Text.InstalledFontCollection();
                int i = 0;
                #region
                foreach (System.Drawing.FontFamily family in fonts.Families)
                {
                    bool isok = true;
                    for (int j = 0; j < nook.Length; j++)
                    {
                        if (nook[j] == i)
                        {
                            isok = false;
                        }
                    }
                    if (isok)
                    {
                        Rectangle rect = new Rectangle(0, 0, imgsize, imgsize);
                        Graphics dg = this.CreateGraphics();
                        Image image = new Bitmap(rect.Width, rect.Height, dg);
                        Graphics ig = Graphics.FromImage(image);
                        ig.FillRectangle(Brushes.Black, rect);
                        Font font = new Font(family.Name, fontsize);
                        Brush brush = System.Drawing.Brushes.White;
                        ig.DrawString(NunberToChar(k), font, brush, 15, 15);
                        //ImageFormat iformat=new ImageFormat(new Guid(Bmp));
                        image.Save("D://" + dirp + "//" + (k - 55).ToString() + "//" + NunberToChar(k) + "_" + i.ToString() + ".jpg");
                        dg.DrawImage(image, new PointF(imgsize, imgsize));
                    }
                    i++;
                }
                #endregion
            }
        }
        private string NunberToChar(int number)
        {

            int num = number;
            System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
            byte[] btNumber = new byte[] { (byte)num };
            return asciiEncoding.GetString(btNumber);

        }/* 何问起 hovertree.com */

    }
}

生成文件夹和里面的图片
在这里插入图片描述
在这里插入图片描述

2.c++ opencv 处理生成最终的训练数据集

依据上一步生成的图片,字体大小不一,宽高比也不相同,而数据的要求是输入图像归一处理为相同的尺寸,且宽=高,同时为了训练的有效,需要尽可能确保训练数据集接近实际测试的数据集,所以还要对生成的图片完成字体区域的提取,变形,扭曲,字体颜色与背景颜色的随机修改,代码如下:

/*********************************************************
生成用于训练的单字符标准数据集合,10个数字,26个字母,7种字体类型
6个字母在一张图上,图片总数为:(36/6)*7*(255/25)*(255/5)
每张图片在生成是二值化阈值随机生成,实现字符边缘随机侵蚀,
背景色和 字体色会遍历选取,确保样本多样性。同时生成标注文件.txt
*/

#include"OPENCV.h"
Mat yuans, ymyy;
ostringstream ossw;//连接存储标注文件的内容(.txt)
ostringstream oss;
ostringstream ossSaveName;
ostringstream ossN;

void getPicAng(Mat& src);///对字体进行小量的随机变形,使用四点透视变形的算法 完成
Mat getcharRect(Mat Img);//从灰度图中获取字体的区域
void getRandomMat(Mat& ImgIn, Mat& ImgOut, int backC, int charC);///对字符区域的前后景随机渲染

int main(int, char** argv)
{
    string savePath = "D://ziliao//mycharkind//";//文件保存的地址

      int rectW = 100;//最终字符的高
    int rectH = 55;//最终字符的宽
    for (int M = 0; M < 37; M++) ///遍历每个字符的37个字体种类 
    {

        //Mat charM5[36];
        for (int N = 0; N < 36; N++) ///遍历36个字符分类
        {
            oss << "D://Charimage//" << N;
            oss << "/*.jpg";         //图片名字后缀,oss可以结合数字与字符串
            string pattern = oss.str();        //oss.str()输出oss字符串,并且赋给pattern
            oss.str("");
            vector<Mat> input_images;
            vector<String> input_images_name;
            glob(pattern, input_images_name, false);
            int all_num = input_images_name.size();
            //------------------------------------------//
            Mat charMs = imread(input_images_name[M]);///存储6张字符原图



            for (int v = 255; v > 0; v = v - 30)遍历选择图像的背景色
            {
                int chC = 10;
                for (int f = 255; f > 0; f = f - chC)遍历选择图像的字体颜色
                {
                    Mat charM = charMs.clone();
                    if (abs(v - f) < 30) {
                        chC = 15;
                    }
                    else {
                        chC = 30;
                    }
                    if (abs(v - f) > 20) {//背景色与字体颜色中值差必须大于5,防止颜色太过接近影响训练
                        -----------------------------------
                        rectH = 100
                        rectW = 55;
                        Mat d_Img = getcoloMat( rectW, rectH, 0);
                        string labTxt = "";
                        vector<int> sxli;
                        vector<int> syli;
                    
                        cvtColor(charM, charM, COLOR_BGR2GRAY);

                        threshold(charM, charM, 0, 255, THRESH_BINARY);//图像二值化
                        charM = getcharRect(charM);
                        getPicAng(charM);

                        int  charH = rectH * ((double)random(70, 85) / (double)100);
                        int chrectW= rectW * ((double)random(70, 85) / (double)100);
                        int cW = (charM.cols * charH) / charM.rows;//依据实际的图像特征调整字符的大小和长宽比
                        cW = cW <= chrectW ? cW : chrectW;
                        resize(charM, charM, Size(cW, charH));
                        int xChar= (rectW- cW)* ((double)random(50, 100) / (double)100);
                        int yChar = (rectH - charH) * ((double)random(50, 100) / (double)100);

                        charM.copyTo(d_Img(Rect(xChar, yChar,  cW,charH )));


                    ///-------------------------------------------//
                    ///随机处理含有6个字符的图片并保存
                        Mat optImg;
                        getRandomMat(d_Img, optImg, v, f);///随机处理
                       //medianBlur(optImg, optImg, 5);//中值模糊
                        GaussianBlur(optImg, optImg, Size(7, 7), 5, 5);//高斯模糊
                        int id = N < 24 ? N : N - 1;
                        if (N!=24) {
                            ossSaveName << savePath << id << "//Char_" << M << N << v << f;
                            string strImg = ossSaveName.str();
                            ossSaveName.str("");
                            imwrite(strImg + ".jpg", optImg);

                            imshow("ss", optImg);
                            d_Img = getcoloMat(rectH, rectW, 0);
                            labTxt = "";
                            waitKey(1);
                        }
                 
                    }


                }

            }
        }


    }


}
/// <summary>
/// 对字体进行小量的随机变形,使用四点透视变形的算法 完成
/// </summary>
/// <param name="ImgIn">输入图像</param>
/// <param name="ImgOut">输出图像</param>
/// <param name="backC">背景灰度值的中值</param>
/// <param name="charC">字体灰度值的中值</param>
void getRandomMat(Mat& ImgIn, Mat& ImgOut, int backC, int charC)
{
    int colorbW = random(5, 15);///随机生成背景色或者前景色的分布宽度的1/2
    int colorfW = random(3, 10);///随机生成背景色或者前景色的分布宽度的1/2


    Mat d_Img = ImgIn.clone();
    const int channels = d_Img.channels();
    int nRows = d_Img.rows;
    int nCols = d_Img.cols * channels;
    //用指针访问像素,速度最快
    uchar* p;
    for (int i = 0; i < nRows; i++)
    {
        p = d_Img.ptr<uchar>(i);//获取每行首地址
        for (int j = 0; j < nCols; ++j)
        {
            if (p[j] > 100)
            {
                int Max = (charC + colorfW);
                int Min = charC - colorfW;

                Max = Max < 255 ? Max : 255;
                Min = Min > 0 ? Min : 0;
                int kp = random(Min, Max);

                p[j] = kp;
            }
            else {
                int Max = backC + colorbW;
                int Min = backC - colorbW;
                Max = Max < 255 ? Max : 255;
                Min = Min > 0 ? Min : 0;
                int kp = random(Min, Max);
                p[j] = kp;
            }



        }
    }
    ImgOut = d_Img;
}

/// <summary>
/// 查找灰度图中的第一个轮廓区域
/// </summary>
/// <param name="Img">目标图像</param>
/// <returns>返回查找到的第一个轮廓区域</returns>
Mat getcharRect(Mat Img)
{
    Mat threshold_output = Img.clone();

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(threshold_output, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));
    vector<vector<Point> > contours_poly(contours.size());
    vector<Rect> boundRect(contours.size());
    vector<Point2f>center(contours.size());
    vector<float>radius(contours.size());
    for (size_t i = 0; i < contours.size(); i++)

    {
        approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
        boundRect[i] = boundingRect((Mat)contours[i]);
        minEnclosingCircle(contours_poly[i], center[i], radius[i]);

    }

    Mat reM;

    Img(boundRect[0]).copyTo(reM);
    return reM;
}

/// <summary>
/// 随机透视变化图像。实现字符扭曲
/// </summary>
/// <param name="src">处理图像</param>
void getPicAng(Mat& src)
{
    Mat dst_warp, dst_warpRotateScale, dst_warpTransformation, dst_warpFlip;
    Point2f srcPoints[4];//原图中的四点 ,一个包含三维点(x,y)的数组,其中x、y是浮点型数
    Point2f dstPoints[4];//目标图中的四点  

    ///完整选择图像原图
    srcPoints[0] = Point2f(0, 0);
    srcPoints[1] = Point2f(0, src.rows);
    srcPoints[2] = Point2f(src.cols, 0);
    srcPoints[3] = Point2f(src.cols, src.rows);

    (double)random(0, 10) / (double)100;
    int ap1 = 5;
    int ap2 = 95;
    //映射后的四个坐标值
    dstPoints[0] = Point2f(src.cols * ((double)random(0, ap1) / (double)100), src.rows * ((double)random(0, ap1) / (double)100));
    dstPoints[1] = Point2f(0 + src.cols * ((double)random(0, ap1) / (double)100), src.rows * ((double)random(ap2, 100) / (double)100));
    dstPoints[2] = Point2f(src.cols * ((double)random(ap2, 100) / (double)100), 0 + src.rows * ((double)random(0, ap1) / (double)100));
    dstPoints[3] = Point2f(src.cols * ((double)random(ap2, 100) / (double)100), src.rows * ((double)random(ap2, 100) / (double)100));

    Mat M1 = getPerspectiveTransform(srcPoints, dstPoints);//由四个点对计算透视变换矩阵  


    warpPerspective(src, dst_warp, M1, src.size());//仿射变换  
    src = dst_warp;


}


生成 结果:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值