利用potracelib静态库批量生成eps矢量图

一、在工程中配置potracelib静态库

新建一个工程,在添加的头文件处加入编译好的静态链接库,
在这里插入图片描述
并通过Project–>右键Build options–>Search directories:(头文件的位置)
在这里插入图片描述
以及Project–>Build options–>Linker settings:(加载库文件)设好置 编译器工程的库和头文件的搜索路径,现在就可以在新工程中使用编译好 的potracelib静态库了。
在这里插入图片描述

二、代码实现

1、为了批量生成矢量图,先编写满足从文件夹中读取所有的bmp文件的方法。

  //GetAllFiles方法用来获取指定路径下所有的文件名
    void  GetAllFiles( string path, vector<string>& files)
    {
        //文件句柄
        long   hFile   =   0;
        //定义_finddata_t结构体fileinfo来存储文件信息
        struct _finddata_t fileinfo;
    //定义字符串变量p用来保存文件所在的目录结构和文件名
    string p;
    //首先调用_findfirst方法查找第一个文件,若成功则用返回的句柄调用_findnext方法继续查找其它的文件
        if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) !=  -1)
        {
            do
            {
                //如果该文件的属性是文件夹,则继续遍历该文件里面的内容
    if((fileinfo.attrib &  _A_SUBDIR))
                {
                    //由于在进入一个子目录时,最先搜索到的前两个文件(夹)是"."(当前目录)和".."(上一层目录),因此需要将这两种情况排除掉
    if(strcmp(fileinfo.name,".") != 0  
    &&  strcmp(fileinfo.name,"..") != 0)
                    {
                        //将该文件名添加到字符串p后面
    files.push_back(p.assign(path).append("\\").append(fileinfo.name) );
                        //递归调用GetAllFiles()方法继续遍历该文件夹下的子文件
    GetAllFiles( p.assign(path).append("\\").append(fileinfo.name), files );
                    }
                }
                //否则,说明该文件已经是最内层的文件,
    //调用push_back方法将该文件名添加到字符串p后面
                else
                {
                files.push_back(p.assign(path).append("\\").append(fileinfo.name) );
                }
            //如果查找下一个文件成功,则继续执行循环遍历下一个文件
            }
            while(_findnext(hFile, &fileinfo)  == 0);
    
            _findclose(hFile);
        }

}

方法解释:

该方法有两个参数,第一个为表示指定路径的字符串(string类型),第二个参数为文件夹与文件名称存储变量(vector类型)。
GetAllFiles方法的执行过程中,首先定义一个_finddata_t结构体来存储文件的各种信息,包括文件属性的存储位置、文件创建时间、文件最后一次被访问的时间、文件最后一次被修改的时间、文件的大小、文件名等。接下来调用_findfirst、_findnext和_fineclose方法将硬盘文件的文件信息存储到_finddata_t结构体所表示的内存空间里。
_findfirs方法用来查找第一个文件,如果查找成功,则返回一个long类型的查找用的句柄(即,一个唯一的编号;这个句柄将在_findnext函数中被使用);如果查找失败,则返回-1。_findfirs方法中包含两个参数:第一个参数是filespec,用来标明文件的字符串,可支持通配符(比如:*.c,则表示当前文件夹下的所有后缀为C的文件);第二个参数是fileinfo,作为存放文件信息的结构体的指针。_findfirs方法成功查找到第一个文件后,将该文件的信息放入这个结构体中。
_findnext方法用来继续查找下一个文件,如果查找成功,则返回0,否则返回-1。_findnext方法中包含两个参数:第一个参数是handle,用来保存_findfirst函数返回的句柄;第二个参数是fileinfo,用做文件信息结构体的指针。_findnext方法找到文件后,将该文的件信息放入这个结构体中。
_findclose方法在查找结束后关闭文件句柄,如果关闭成功,则返回0,否则返回-1。_findclose方法包含一个参数handle,用来保存_findfirst函数返回的句柄。
文件查找具体过程包括:首先调用_findfirst方法查找第一个文件,若成功则用hFile保存返回的句柄,调用_findnext方法继续查找下一个文件。采用递归的深度优先搜索遍历每个文件里面的内容:如果该文件的属性是文件夹,则继续调用GetAllFiles方法遍历该文件里面的内容;否则,该文件已经是最内层的文件,直接调用push_back方法将该文件名添加到字符串p后面。最后,文件查找结束,调用_fineclose方法关闭文件句柄hFile。

2、然后结合上文介绍的bmp文件的边缘检测以及图像融合步骤(硬笔字体需要这两步,而软笔字体可以省略)生成新的可以使用的OpenCV:mat。

 //读取源图像并检查图像是否读取成功
	Mat srcImage = imread(bmp_picture[j]);
	if (!srcImage.data)
	{
		cout << "读取图片错误,请重新输入正确路径!\n";
		system("pause");
		return -1;
	}
	imshow("【源图像】", srcImage);
	//灰度转换
	Mat srcGray;
	cvtColor(srcImage, srcGray, CV_RGB2GRAY);
	imshow("【灰度图】", srcGray);
	//初始化相关变量
	//初始化自适应阈值参数
	Mat dstImage;
	const int maxVal = 255;
	int blockSize = 3;	//取值3、5、7....等
	int constValue = 10;
	int adaptiveMethod = 1;
	int thresholdType = 0;
	/*
		自适应阈值算法
		0:ADAPTIVE_THRESH_MEAN_C
		1:ADAPTIVE_THRESH_GAUSSIAN_C
		--------------------------------------
		阈值类型
		0:THRESH_BINARY
		1:THRESH_BINARY_INV
	*/
//	图像自适应阈值操作
//	adaptiveThreshold(srcGray, dstImage, maxVal, adaptiveMethod, thresholdType, blockSize, constValue);
//
//	imshow("【自适应阈值】", dstImage);
//
	 //scharr边缘检测
    Mat image_scharr ;
    Scharr(srcGray, image_scharr, CV_8UC1,1, 0);
    imshow("scharr 边缘检测", image_scharr);
	//利用addWeighted()函数对两幅图像进行融合
	Mat newImage(srcGray.size(), srcGray.type());
	//最后融合效果显示在灰度图像上。
	addWeighted(srcGray, 0.5, image_scharr, 0.5, 0., newImage);	
	/*
		若不想毁坏原始srcGray图像,也可建立一个与原始图像类型尺寸一样的新图像,将融合后的图像保存到上面。
		建立方法:
		Mat newImage(srcGray.size(), srcGray.type());	//newImage与srcGray类型尺寸相同
	*/
//	namedWindow("图像1与图像2融合效果图");
        imshow("图像1与图像2融合效果图", newImage);
    waitKey();

3.接下来,通过potrace其他源码文件,找到可以由OpenCV的mat格式生成可以被potracelib使用的bmp文件格式的方法bitmapFromMat(const cv::Mat& image, int threshold).以及一些其他的辅助函数。如下。


//计算给定dy和h的位图数据区域所需的大小(以字节为单位),假设h >= 0。
//如果大小不适合ptrdiff_t类型,则返回-1。
ptrdiff_t getsize(int dy, int h)
{
	ptrdiff_t size;

	if(dy < 0)
	{
		dy = -dy;
	}

	size = (ptrdiff_t)dy * (ptrdiff_t)h * (ptrdiff_t)BM_WORDSIZE;

	/* check for overflow error */
	if(size < 0 || (h != 0 && dy != 0 && size / h / dy != BM_WORDSIZE))
	{
		return -1;
	}

	return size;
}

//返回初始化为0的新位图。NULL错误为error。假设 w, h >= 0.
potrace_bitmap_t *bm_new(int w, int h)
{
	potrace_bitmap_t *bm;
	int dy = w == 0 ? 0 : (w - 1) / BM_WORDBITS + 1;
	ptrdiff_t size;

	size = getsize(dy, h);
	if(size < 0)
	{
		errno = ENOMEM;
		return NULL;
	}
	if(size == 0)
	{
		size = 1; //确保calloc()不返回NULL


	bm = (potrace_bitmap_t *)malloc(sizeof(potrace_bitmap_t));
	if(!bm)
	{
		return NULL;
	}
	bm->w = w;
	bm->h = h;
	bm->dy = dy;
	bm->map = (potrace_word *)calloc(1, size);
	if(!bm->map)
	{
		free(bm);
		return NULL;
	}
	return bm;
}

}
//释放位图空间
void bm_free(potrace_bitmap_t *bm)
{
	if(bm != NULL)
	{
		free(bm->map);
	}
	free(bm);
}

// 仅适用于CV_8UC1二进制或灰度图像
potrace_bitmap_t* bitmapFromMat(const cv::Mat& image, int threshold)
{
	potrace_bitmap_t *bitmap = bm_new(image.cols, image.rows);
	int pi = 0;
	for(int row = 0; row < image.rows; ++row)
	{
		const uchar* ptr = image.ptr<uchar>(image.rows - 1 - row);
		for(int col = 0; col < image.cols; ++col)
		{
			if(ptr[col] > threshold)
				BM_PUT(bitmap, col, row, 0);
			else
				BM_PUT(bitmap, col, row, 1);
		}
	}
	return bitmap;
}

需要注意的不是所有的Mat都可以生成potrace算法可以利用的bitmap,而是只有CV_8UC1二进制格式的Mat或灰度图像才可以,所以在第一步处理bmp图片时检测边缘或者图片融合后得到的Mat格式需要设置为CV_8UC1或者直接利用原图像的灰度图像。生成potrace算法可以利用的bitmap之后,按照设置跟踪参数、跟踪位图、将矢量图形写入文件的步骤,利用potracelib中的方法加以实现。这里参考了potracelib_demo.c源文件中的代码。

 //生成potrace可以利用的bmp格式
  bm = bitmapFromMat(srcGray, 0);
  //从默认值开始设置跟踪参数
  param = potrace_param_default();
  if (!param) {
    fprintf(stderr, "Error allocating parameters: %s\n", strerror(errno));
    return 1;
  }
  param->turdsize = 0;
  //跟踪位图
  st = potrace_trace(param, bm);
  if (!st || st->status != POTRACE_STATUS_OK) {
    fprintf(stderr, "Error tracing bitmap: %s\n", strerror(errno));
    return 1;
  }
    cout<<"跟踪位图已完毕"<<endl;
  //打开文件,写入矢量图形内容
    string svg_file = bmp_picture[j].substr(0,bmp_picture[j].find('.'))+".eps";
    FILE* file = fopen(svg_file,"w");
    if(!file)
        return -1;
  //输出向量数据,例如作为一个基本的EPS文件
  fprintf(file,"%%!PS-Adobe-3.0 EPSF-3.0\n");
  fprintf(file,"%%%%BoundingBox: 0 0 %d %d\n", bm->w, bm->h);
  fprintf(file,"gsave\n");
  //画出每一个曲线
  p = st->plist;
  while (p != NULL) {
    n = p->curve.n;
    tag = p->curve.tag;
    c = p->curve.c;
    fprintf(file,"%f %f moveto\n", c[n-1][2].x, c[n-1][2].y);
    for (i=0; i<n; i++) {
      switch (tag[i]) {
      case POTRACE_CORNER:
	fprintf(file,"%f %f lineto\n", c[i][1].x, c[i][1].y);
	fprintf(file,"%f %f lineto\n", c[i][2].x, c[i][2].y);
	break;
      case POTRACE_CURVETO:
	fprintf(file,"%f %f %f %f %f %f curveto\n",
	       c[i][0].x, c[i][0].y,
	       c[i][1].x, c[i][1].y,
	       c[i][2].x, c[i][2].y);
	break;
      }
    }
    //在一组正向路径及其反向子路径的末尾填充
    if (p->next == NULL || p->next->sign == '+') {
      fprintf(file,"0 setgray fill\n");
    }
    p = p->next;
  }
  fprintf(file,"grestore\n");
  fprintf(file,"%%EOF\n");

4.最后将通过potrace算法生成的轨迹写入同名的eps文件。

 //打开文件,写入矢量图形内容
    string svg_file = bmp_picture[j].substr(0,bmp_picture[j].find('.'))+".eps";
    FILE* file = fopen(svg_file,"w");
    if(!file)
        return -1;

5.结果如下
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值