在这之前,我们需要了解一下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