1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > python opencv读取图像并生成plt文件

python opencv读取图像并生成plt文件

时间:2018-10-14 10:05:38

相关推荐

python opencv读取图像并生成plt文件

1 前言

在数控系统中,plt文件是标准的数控加工文件格式。一般可由signMast、文泰等工控软件生成plt加工文件。现在假设电脑或手机上没有工控软件,只有一些描述加工路径的图片,此时可以用opencv提取轮廓来生成加工路径,并将路径保存成plt文件。使用python版的opencv库可以快速搞定这个功能。

2 轮廓的提取

轮廓的提取

先用网上搜到的提取轮廓最简单的几步:

import cv2import numpyimport osdef show_img(window,img):cv2.namedWindow(window,0)cv2.resizeWindow(window,480,640)cv2.imshow(window,img)img = cv2.imread("D:/iphone.png")img = cv2.bitwise_not(img)#转为灰度图gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#图像二值化ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY| cv2.THRESH_OTSU)#得到轮廓contours,hierarchy = cv2.findContours(thresh,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_TC89_L1)draw_img_1 = numpy.ones(img.shape,dtype=numpy.uint8) cv2.drawContours(draw_img_1,contours,-1,(0,0,255),1)show_img('contours',draw_img_1)cv2.waitKey(0)

原图片 iphone.png:

提取到的轮廓图 draw_img_1:

仔细看可以发现这里为每个图形对象找到了两个轮廓。由于图片中的线条有宽度,所以Opencv会为每个图形对象找到了2个轮廓,分别是线条外边构成的外轮廓和线条另外一边构成的内轮廓。现在的任务就是怎么去掉另外的重复轮廓。

去除重复轮廓

研究cv2.findContours函数:

contours,hierarchy= cv2.findContours(image,mode,method)输入:image:带有轮廓信息的图像;mode:提取轮廓后,输出轮廓信息的组织形式,可以取以下值:cv2.RETR_EXTERNAL:输出轮廓中只有外侧轮廓信息;cv2.RETR_LIST:以列表形式输出轮廓信息,各轮廓之间无等级关系;cv2.RETR_CCOMP:输出两层轮廓信息,即内外两个边界(下面将会说到contours的数据结构);cv2.RETR_TREE:以树形结构输出轮廓信息。method:指定轮廓的近似办法,有以下选项cv2.CHAIN_APPROX_SIMPLE:指定轮廓的近似办法,有以下选项:cv2.CHAIN_APPROX_NONE:存储轮廓所有点的信息,相邻两个轮廓点在图象上也是相邻的;cv2.CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标;cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法保存轮廓信息。输出:python3里返回2个值:contours,hierarchycontours:list结构,列表中每个元素代表一个边沿信息。每个元素是(x,1,2)的三维向量,x表示该条边沿里共有多少个像素点,第三维的那个“2”表示每个点的横、纵坐标;hierarchy:返回类型是(x,4)的二维ndarray。x和contours里的x是一样的意思。如果输入选择cv2.RETR_TREE,则以树形结构组织输出,hierarchy的四列分别对应同级下一个轮廓编号、同级上一个轮廓编号、子轮廓编号、父轮廓编号,该值为负数表示没有对应项。

重点需要关注返回的hierarchy参数。它描述了轮廓的层次信息。当mode采用cv2.RETR_CCOMP参数后,findContours只会取找到两层轮廓。对于ihone.png图片,hierarchy仿真控制台打印结果是:

这里共有8个轮廓。轮廓1的父轮廓是轮廓0;轮廓3的父轮廓是轮廓2;轮廓5的父轮廓是轮廓4;轮廓7的父轮廓是轮廓0。轮廓0,2,4,6都没有父轮廓,意味着他们是最顶级轮廓。由此可以看出这里轮廓0,2,4,6是要提取的轮廓,轮廓1,3,5,7分别是多出来的重复内层轮廓。因此程序中是否可以采用hierarchy数组元素的第4个子元素hierarchy[i][3]为-1的条件来判断,hierarchy[i][3]为-1就是顶层轮廓,只要hierarchy[i][3]不为-1就是内层轮廓就可以去掉该轮廓了吗?但是还需要考虑另外一种情况,看这张abcd.png图片:

提取出来的hierarchy打印如下:

这里字母ABCD共提取了8条轮廓,字母A由2条轮廓,字母B有3条轮廓,字母C由1条轮廓,字母D由2条轮廓。若是采用刚才的判断条件,就只剩下4条轮廓了,这明显不对。在这里的内层轮廓也需要保留。这是因为iphone图片的内层轮廓是由线条宽度引入进来的,和外层轮廓相似度非常高,这张字母图片的内层轮廓是字母固有的内层轮廓,和外层轮廓的差别比较大。可以用轮廓长度的相似度来区分这俩种情况。下面代码当子轮廓和父轮廓轮廓长度比值小于0.8时就认为是不同轮廓。

points_arr = []for i in range(len(hierarchy[0])-1,-1,-1):item = hierarchy[0][i]if item[3] == -1:cv2.drawContours(draw_img_1,contours,i,(0,0,255),1)points_arr.append(contours[i])else:father_id = item[3]l1 = cv2.arcLength(contours[father_id],True)l2 = cv2.arcLength(contours[i],True)if l2/l1<0.8:cv2.drawContours(draw_img_1,contours,i,(0,0,255),1)points_arr.append(contours[i])

绘制加工轨迹

提取出所有轮廓以后,就可以生成加工路径了。每条轮廓都是由加工点构成的闭合路径。为了减小轮廓点数,所以在findContours函数中未采用cv2.CHAIN_APPROX_NONE参数,而是采用cv2.CHAIN_APPROX_TC89_L1近似算法来保留关键拐点。对于同一条轮廓,从第1个点进入以后走完该轮廓所有点,还需要回到最初点才构成闭合路径。当走完第1条轮廓路径以后,接着走第2条轮廓路径。

#绘制线条加工轨迹到draw_img_2中draw_img_2 = numpy.zeros(img.shape,dtype=numpy.uint8) for i in range(len(points_arr)):points = points_arr[i]old_point = points[0]for point in points:cv2.line(draw_img_2,(old_point[0][0],old_point[0][1]),(point[0][0],point[0][1]),(0,255,0),1)old_point = pointcv2.line(draw_img_2,(old_point[0][0],old_point[0][1]),(points[0][0][0],points[0][0][1]),(0,255,0),1)if i<len(points_arr)-1:cv2.line(draw_img_2,(points_arr[i][0][0][0],points_arr[i][0][0][1]),(points_arr[i+1][0][0][0],points_arr[i+1][0][0][1]),(255,0,0),1)show_img('animation',draw_img_2)cv2.imwrite('contours2.png',draw_img_2)

生成plt文件

生成plt文件就是跟绘制加工轨迹相似的方法。就是注意下刀和台刀。走刀第每条路径第1个点时必然是抬刀,使用PU;在路径中走动是下刀,使用PD。走完所有路径以后,需要回到(0,max_y)点,这里的max_y是所有加工路径中纵坐标最大值。此外函数中设置了一个比值ratio,代表1像素对应多少毫米。将像素换成毫米。在plt文件中1毫米代表40。这是程序中又乘以了40的由来。

def writePoint(f,point,height,flag,ratio):if(flag == 1):f.write('PD%d,%d;' % (int((point[0][1])*ratio*40),int(point[0][0]*ratio*40)))else:f.write('PU%d,%d;' % (int((point[0][1])*ratio*40),int(point[0][0]*ratio*40)))#ratio:,像素到毫米的换算比例,1像素=ratio毫米def createPltFile(arr,height,ratio):f = open('text.plt','w+')f.write('IN;SP1;')max_y = 0writePoint(f,points_arr[0][0],height,0,ratio)max_y = max(points_arr[0][0][0][1],max_y)for i in range(len(points_arr)):points = points_arr[i]first_point = points[0]for point in points:writePoint(f,point,height,1,ratio)max_y = max(point[0][1],max_y)writePoint(f,first_point,height,1,ratio)max_y = max(first_point[0][1],max_y)if i<len(points_arr)-1:writePoint(f,points_arr[i+1][0],height,0,ratio)max_y = max(points_arr[i+1][0][0][1],max_y)writePoint(f,[[0,max_y]],height,0,ratio)f.write('PG;@@@@@@@@@@@@@@@@@@@;')

完整程序:

import cv2import numpyimport osdef show_img(window,img):cv2.namedWindow(window,0)cv2.resizeWindow(window,480,640)cv2.imshow(window,img)def writePoint(f,point,height,flag,ratio):if(flag == 1):f.write('PD%d,%d;' % (int((point[0][1])*ratio*40),int(point[0][0]*ratio*40)))else:f.write('PU%d,%d;' % (int((point[0][1])*ratio*40),int(point[0][0]*ratio*40)))#ratio:,像素到毫米的换算比例,1像素=ratio毫米def createPltFile(arr,height,ratio):f = open('text.plt','w+')f.write('IN;SP1;')max_y = 0writePoint(f,points_arr[0][0],height,0,ratio)max_y = max(points_arr[0][0][0][1],max_y)for i in range(len(points_arr)):points = points_arr[i]first_point = points[0]for point in points:writePoint(f,point,height,1,ratio)max_y = max(point[0][1],max_y)writePoint(f,first_point,height,1,ratio)max_y = max(first_point[0][1],max_y)if i<len(points_arr)-1:writePoint(f,points_arr[i+1][0],height,0,ratio)max_y = max(points_arr[i+1][0][0][1],max_y)writePoint(f,[[0,max_y]],height,0,ratio)f.write('PG;@@@@@@@@@@@@@@@@@@@;')img = cv2.imread("D:/iphone.png")img = cv2.bitwise_not(img)#转为灰度图gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)kernel = numpy.ones((3, 3), numpy.uint8)img_dilate = cv2.dilate(gray, kernel,iterations = 3)img_dilate = cv2.erode(gray, kernel,iterations = 3)#gray = cv2.Canny(gray, 100, 300)#图像二值化ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY| cv2.THRESH_OTSU)#得到轮廓contours,hierarchy = cv2.findContours(thresh,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_TC89_L1)draw_img_1 = numpy.ones(img.shape,dtype=numpy.uint8) index = 0points_arr = []for i in range(len(hierarchy[0])-1,-1,-1):item = hierarchy[0][i]if item[3] == -1:cv2.drawContours(draw_img_1,contours,i,(0,0,255),3)points_arr.append(contours[i])else:father_id = item[3]l1 = cv2.arcLength(contours[father_id],True)l2 = cv2.arcLength(contours[i],True)if l2/l1<0.8:cv2.drawContours(draw_img_1,contours,i,(0,0,255),3)points_arr.append(contours[i])#cv2.drawContours(draw_img_1,contours,-1,(0,0,255),1)show_img('input',img)show_img('gray',gray)show_img('thresh',thresh)show_img('contours',draw_img_1)cv2.imwrite('contours.png',draw_img_1)#绘制线条加工轨迹到draw_img_2中draw_img_2 = numpy.zeros(img.shape,dtype=numpy.uint8) for i in range(len(points_arr)):points = points_arr[i]old_point = points[0]for point in points:cv2.line(draw_img_2,(old_point[0][0],old_point[0][1]),(point[0][0],point[0][1]),(0,255,0),3)old_point = pointcv2.line(draw_img_2,(old_point[0][0],old_point[0][1]),(points[0][0][0],points[0][0][1]),(0,255,0),3)if i<len(points_arr)-1:cv2.line(draw_img_2,(points_arr[i][0][0][0],points_arr[i][0][0][1]),(points_arr[i+1][0][0][0],points_arr[i+1][0][0][1]),(255,0,0),3)show_img('animation',draw_img_2)cv2.imwrite('contours2.png',draw_img_2)createPltFile(points_arr,img.shape[0],1.0/10)cv2.waitKey(0)

当读入iphone.png时执行结果:

当读入abcd.png时执行结果:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。