利用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.結果如下
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章