基于OpencV的轮廓填充算法在3D打印机中的应用

       在这之前,我们需要了解一下SLC文件的格式,只有对格式有一点了解,我们才能做接下来的工作,首先SLC文件中是通过描述各层中的多段线来描述整个模型的,多段线之间两两相连。对单个轮廓来说,最后一点必须等于第一点的坐标,同一组多段线头尾坐标是一样的,另外同一层可能会有多组多段线,为了描述方便,下面用“轮廓”一词来代替“多段线”。SLC文件携带的有用信息有模型XYZ方向的最大与最小坐标、层厚(单位:mm)、当前层高(单位:mm)、轮廓坐标(单位:mm),所以文件未携带的参数是要通过设备一侧来进行设置的。

       SLC文件格式说明:https://chenjy1225.github.io/2018/03/16/slc-file-format/
       
     上图表示的是两组轮廓,一个顺时针,一个逆时针,通过顺逆时针的走向来确定填充的位置,从图中可以看出,填充的位置始终是轮廓走向的左侧。

下图是实际读取到的其他文件中的其中一组轮廓坐标,它的头尾坐标是一样的
                                             




代码

//OpenCV的轮廓查找和填充  https://blog.csdn.net/garfielder007/article/details/50866101
//opencv findContours和drawContours使用方法  https://blog.csdn.net/ecnu18918079120/article/details/78428002
//OpenCV关于容器的介绍  https://blog.csdn.net/Ahuuua/article/details/80593388

//opencv
#include "opencv2/highgui/highgui.hpp"  
#include "opencv2/imgproc/imgproc.hpp" 
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>   
#include <fcntl.h>
#include <unistd.h>		//write read等函数
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/fs.h>
#include <stdio.h>
#include <iostream>  
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <pthread.h>  	//包线程要包含
#define UNIX_DOMAIN "/tmp/UNIX.domain"
//

using namespace cv;
using namespace std;

char file_continue_flag = 0; //解析标志,为1则继续解析,覆盖原来的图片

float z_level = 0;


//字符数组中查找字符串或字符数组
//pSrc:原字符
//srcSize:原字符长度
//pDest:比对的字符
//dstSize:比对的字符的长度
int FindString(char pSrc[], int srcSize, char pDest[], int dstSize)
{
    int iFind = -1;
    for(size_t i=0;i<(size_t)srcSize;i++){
        int iCnt = 0;
        for (size_t j=0; j<(size_t)dstSize; j++) {
            if(pDest[j] == pSrc[i+j])
                iCnt++;
        }
        if (iCnt==dstSize) {
            iFind = i;
            break;
        }
    }
    return iFind;//返回比对的字符起始位置
}

void OpenSLC(const char file_dir[])
{
	FILE *fd_temp;  //将数据保存到文本区

	//保存调试数据到文件夹
	fd_temp=fopen("./temp.txt", "w+");
	
	/************处理文件头的字符变量,解析完毕后,关闭当前文件*********************/
	//关于文件的文件头变量
	char str_temp[300];
	float minx, maxx, miny, maxy, minz, maxz;

	FILE * fid = fopen(file_dir,"r");//用于处理文件头的信息
	if(fid == NULL)
    {
        return;
    }
	//此处添加文件头处理方式
	fread(str_temp,sizeof(char),300,fid);
	//-EXTENTS <minx,maxx miny,maxy minz,maxz> CAD模型 x,y,z轴的范围
	int ssss = FindString(str_temp,300,(char *)"-EXTENTS",8);
	strncpy(str_temp,str_temp+ssss,100);//提取出XYZ的范围数据
	
	//提取XYZ的范围(前6个浮点值)
	char str[100];//无关变量
	sscanf(str_temp, "%[(A-Z)|^-]%f%[(^ )|(^,)]%f%[(^ )|(^,)]%f%[(^ )|(^,)]%f%[(^ )|(^,)]%f%[(^ )|(^,)]%f%[(A-Z)|^*]", str, &minx, str, &maxx, str, &miny, str, &maxy, str, &minz, str, &maxz, str);
	printf("%.3f %.3f %.3f %.3f %.3f %.3f\r\n", minx,maxx,miny,maxy, minz, maxz);

	// printf("%s\r\n", str_temp);
	fprintf(fd_temp,"%s\r\n",str_temp);
	fclose(fid);
	/*********************************************************************************/
	
	
	int fd; 		//文件描述符
	char m_data;	//读取到的数据
	float d = 0;    //d > 0 从上往下看是逆时针
	int size=0;		//读取到的数据长度
	
	fd = open(file_dir, O_RDONLY);
	//

	
	/*
	// O_CREAT 若欲打开的文件不存在则自动建立该文件。
	// O_RDONLY 以只读方式打开文件
	// O_WRONLY 以只写方式打开文件
	*/
	
	//【1】CV_8UC1---则可以创建----8位无符号的单通道---灰度图片------grayImg
	//【2】CV_8UC3---则可以创建----8位无符号的三通道---RGB彩色图像---colorImg 
	//【3】CV_8UC4--则可以创建-----8位无符号的四通道---带透明色的RGB图像 
	Mat dst = Mat::zeros(1920, 1080, CV_8UC1);//生成的图片,其分辨率由实际的FrameBuffer来决定
	CvScalar color=cvScalar(0);
	
	vector<Point> contour;       	 //单个轮廓坐标值
	vector<vector<Point>> v_contour; //当前层所有轮廓集合
	
	
	vector<int> flag_swap_vector;	//轮廓排序用
	vector<vector<Point>> vctint;	//轮廓排序用
	float flag_swap=0;				//轮廓排序用
	
	

	unsigned int i=0;
	unsigned int j=0;

	unsigned int n_boundary,n_vertices,n_gaps;
	float   n_float,n_layer;

	float   n_polylineX,n_polylineY;
	
    /**************************处理头文件部分**************************/
	while(1)
	{
		i++;
		if(i==2048)
		{
			printf("file error\r\n");
			fprintf(fd_temp,"file error\r\n");
			close(fd);
			
			//关闭调试输出的数据文件
			fclose(fd_temp);
			//
			return;
		}

		size = read(fd, &m_data, 1);
		// printf("m_data=%x\r\n", m_data);

		switch(m_data)
		{
		case 0x0d:
			j=1;
			break;
		case 0x0a:
			if(j==1)
				j=2;
			break;
		case 0x1a:
			if(j==2)
				j=3;
			break;
		default:
			j=0;
			break;
		}
		if(j==3)
			break;
	}
	
	printf("size=%d\r\n", size);
	/******************************************************************/
	/***************************处理预留部分***************************/
	for(i=0;i<256;i++)
	{
		size = read(fd, &m_data, 1);
		// fprintf(fd_temp,"m_data=%x\r\n",m_data);
	}
	/******************************************************************/
	/**************************处理样本表部分**************************/
	size = read(fd, &m_data, 1);
	// printf("Sampling Table Size=%x\r\n", m_data);
	// fprintf(fd_temp,"Sampling Table Size=%x\r\n",m_data);
	while(m_data)
	{
		size = read(fd, &n_float, 4);//Minimum Z Level
		size = read(fd, &n_float, 4);//Layer Thickness
		// printf("Layer Thickness=%.5f\r\n",n_float);
		fprintf(fd_temp,"Layer Thickness=%.5f\r\n",n_float);
		
		// m_parameter->n_HLayer=n_float;
		//n_totalLayers=(int)((zmax-zmin)/n_float);    //计算出来的总层数
		size = read(fd, &n_float, 4);    //Line Width Compensation
		size = read(fd, &n_float, 4);    //Reserved
		m_data--;
	}
	
	int sss=0;
	// /******************************************************************/
	// /*************************处理轮廓数据部分*************************/
	while(1)
	{
		sss++;
		size = read(fd, &n_layer, 4);

		fprintf(fd_temp,"Z轴高度=%.5f\r\n",n_layer);
		
		size = read(fd, &n_boundary, 4);


		fprintf(fd_temp,"轮廓数=%d\r\n",n_boundary);
		if(n_boundary==0xFFFFFFFF)  //结束符
			break;
		
		for(i=0;i<n_boundary;i++)   //把同一层多个轮廓都放在同一容器中,
		{                           //显示跟数据处理时 要根据起始点和同轮廓的终点相等来判断是否为同一轮廓  
			size = read(fd, &n_vertices, 4);//一个轮廓环中的点数

			fprintf(fd_temp,"第%d个轮廓环中的点数=%d\r\n",i+1,n_vertices);

			size = read(fd, &n_gaps, 4);

			contour.clear();//删除容器中的所有元素
			for(j=0;j<n_vertices;j++)
			{
				size = read(fd, &n_polylineX, 4);
		
				fprintf(fd_temp,"{%.0f,",(n_polylineX-minx)*10); //偏移后的坐标放大,保存调试数据到文件
				
				size = read(fd, &n_polylineY, 4);
	
				fprintf(fd_temp,"%.0f}\r\n",(n_polylineY-miny)*10); //偏移后的坐标放大,保存调试数据到文件
				contour.push_back(Point((long)((n_polylineX-minx)*10),(long)((n_polylineY-miny)*10))); //向轮廓坐标尾部添加点坐标

			}

			v_contour.push_back(contour);//追加当前轮廓数据到当前层容器变量中
			contour.clear();//删除容器中的所有元素		  
		}
		

//
		//通过冒泡法实现容器中轮廓的排序,使得较小轮廓始终位于较大轮廓后,能够判断是否出现交叉异常(注:两个分离的轮廓也会进行排序,不影响填充)
		int n; //需要排序的轮廓个数
		n=v_contour.size();//获取轮廓的个数

		for(size_t cmpnum = n-1; cmpnum != 0; --cmpnum)
		{
			for (size_t i = 0; i != cmpnum; ++i)
			{	
				for(size_t k=0;k<v_contour[i+1].size();k++)
				{
	
					flag_swap=pointPolygonTest(v_contour[i], v_contour[i+1][k], false); // 对于每个点都去检测 
					flag_swap_vector.push_back(flag_swap);
				}

				for(size_t z=0;z<flag_swap_vector.size()-1;z++)
				{
					if(flag_swap_vector[z]!=flag_swap_vector[z+1])
					{
						printf("有存在交叉现象\r\n");
						//这里应该去做相应的异常处理
					}
				}
				
				flag_swap_vector.clear();//删除容器中的所有元素
				
				if (flag_swap == -1)
				{
					swap(v_contour[i],v_contour[i+1]);
				}
			}
		}
			
//		
		//清除图像
		dst.setTo(Scalar(0));//把像素点值清零
		
		for(i=0;i<n_boundary;i++)   //把同一层多个轮廓都放在同一容器中,
		{                           //显示跟数据处理时 要根据起始点和同轮廓的终点相等来判断是否为同一轮廓  
			d = 0;
			for (size_t j = 0; j < v_contour[i].size()-1; j++)
			{
				d += -0.5*(v_contour[i][j+1].y+v_contour[i][j].y)*(v_contour[i][j+1].x-v_contour[i][j].x);
			}
		
			// a) 存放单通道图像中像素:cvScalar(255);
			// b) 存放三通道图像中像素:cvScalar(255,255,255);
			if(d > 0)
			{
				//cout << "逆时针:counterclockwise"<< endl;
				fprintf(fd_temp,"逆时\r\n\r\n");
				//填充白色
				color=cvScalar(255);
			}
			else
			{
				//cout << "顺时针:clockwise" << endl;
				fprintf(fd_temp,"顺时\r\n\r\n");
				//填充黑色
				color=cvScalar(0);
			}	
			drawContours( dst,v_contour ,i, color, CV_FILLED );		
		}
		while(!file_continue_flag);
		file_continue_flag = 0;    // 使下次处于一个阻态

		imwrite("./dst.bmp",dst);
		
		v_contour.clear();//删除容器中的所有元素,这里的元素是同一层中所有轮廓数据

		fprintf(fd_temp,"第%d层\r\n",sss);
		printf("第%d层\r\n",sss);
		printf("BMP_OK\r\n");	
	}
	
	fclose(fd_temp);
	close(fd);
}


void *thread_1(void *args)//文件处理
{
	while(1)
	{
		//OpenSLC函数一般情况下为阻态
		OpenSLC("./jcad.slc");
		printf("解析结束\r\n");
		while(1);
	}
	return NULL;
}

void *thread_2(void *args)//外界通信
{
	/*
	内容:1、询问是否解除SLC文件解析阻塞,继续生成位图(注:生成BMP位图比较耗时)
	*/
	while(1)
	{
		getchar();
		file_continue_flag = 1;
	}
	return NULL;
}

int main(void)
{
	int ret=0;
	pthread_t id1,id2;
	
	ret=pthread_create(&id1,NULL,thread_1,NULL);//开启线程	
	if(ret)
	{
		printf("create pthread error!\n");
		return -1; 
	}

	ret=pthread_create(&id2,NULL,thread_2,NULL);//开启线程
	if(ret)
	{
		printf("create pthread error!\n");
		return  -1; 
	}
	pthread_join(id1,NULL);//等待线程id1执行完毕,这里阻塞。等待该线程执行完毕后,继续执行下面的语句,否则从主程序中退出,意味着该程序结束了,线程也就没有机会执行。
	pthread_join(id2,NULL);//等待线程id2执行完毕,这里阻塞。等待该线程执行完毕后,继续执行下面的语句,否则从主程序中退出,意味着该程序结束了,线程也就没有机会执行。
	printf("main over!\n");
	return 0;
}





软件流程图


读取的第一层图像



QQ交流聊:275577611

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
OpenCV提供了很多寻找轮廓的函数,其最常用的是 `findContours` 函数。 下面是一个基于OpenCV轮廓匹配算法的示例代码,其使用了 `findContours` 函数寻找图像轮廓,并使用 `matchShapes` 函数计算不同轮廓之间的距离: ```python import cv2 # 读取模板图像和待匹配图像 template = cv2.imread("template.png") target = cv2.imread("target.png") # 转换为灰度图像 template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) target_gray = cv2.cvtColor(target, cv2.COLOR_BGR2GRAY) # 二值化处理 _, template_thresh = cv2.threshold(template_gray, 127, 255, cv2.THRESH_BINARY) _, target_thresh = cv2.threshold(target_gray, 127, 255, cv2.THRESH_BINARY) # 寻找轮廓 template_contours, _ = cv2.findContours(template_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) target_contours, _ = cv2.findContours(target_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓之间的距离 for template_contour in template_contours: for target_contour in target_contours: distance = cv2.matchShapes(template_contour, target_contour, cv2.CONTOURS_MATCH_I1, 0) print("distance:", distance) ``` 在上述代码,首先读取模板图像和待匹配图像,并将它们转换为灰度图像。然后,对它们进行二值化处理,以便寻找轮廓。接着,使用 `findContours` 函数分别寻找模板图像和待匹配图像轮廓。最后,使用 `matchShapes` 函数计算不同轮廓之间的距离,并输出结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_16728287

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值