1 前言

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

2 轮廓的提取

轮廓的提取

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

import cv2
import numpy
import 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 cv2
import numpy
import os
def 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 = 0
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),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时执行结果:

python opencv读取图像并生成plt文件相关推荐

  1. python opencv读取图像像素值_python-opencv--图像像素通道读取及修改

    data/dtype/size/shape/len ''' import cv2 as cv import numpy as np def access_pixes(image): print(ima ...

  2. python opencv 读取图像检查是否为空

    通过 is None 检查即可 示例代码: import cv2img = cv2.imread(your_path) if img is None:print("your image is ...

  3. python图片保存jpg、show变成bmp_Python 实现判断图片格式并转换,将转换的图像存到生成的文件夹中...

    Python 实现判断图片格式并转换,将转换的图像存到生成的文件夹中 我就废话不多说了,直接上代码吧! import Image from datetime import datetime impor ...

  4. 使用OpenCV python模块读取图像并将其另存为灰度系统

    In Python, we can use an OpenCV library named cv2. Python does not come with cv2, so we need to inst ...

  5. Python OpenCV 之图像乘除与像素的逻辑运算,图像处理取经之旅第 17 天

    今天的学习的内容是:通过 Python OpenCV 对图像实现乘除操作,涉及函数为 cv2.multiply 与 cv2.divide.后面又补充了一些像素的逻辑运算,以及一个综合案例 cv2.mu ...

  6. 【Python OpenCV】图像直方图 calcHist方法 equalizeHist方法

    [Python OpenCV]图像直方图 calcHist方法 equalizeHist方法 (一)图像直方图 图像的构成是有像素点构成的,每个像素点的值代表着该点的颜色(灰度图或者彩色图).所谓直方 ...

  7. Python+OpenCV:图像修复(Image Inpainting)

    Python+OpenCV:图像修复(Image Inpainting) 理论 Most of you will have some old degraded photos at your home ...

  8. Python+OpenCV:图像二进制鲁棒独立基本特征(BRIEF, Binary Robust Independent Elementary Features)

    Python+OpenCV:图像二进制鲁棒独立基本特征(BRIEF, Binary Robust Independent Elementary Features) 理论 We know SIFT us ...

  9. Python+OpenCV:图像快速角点检测算法(FAST Algorithm for Corner Detection)

    Python+OpenCV:图像快速角点检测算法(FAST Algorithm for Corner Detection) 理论 Feature Detection using FAST Select ...

  10. Python+OpenCV:图像Shi-Tomasi角点检测器

    Python+OpenCV:图像Shi-Tomasi角点检测器 理论 The scoring function in Harris Corner Detector was given by: Inst ...

最新文章

  1. 胡阳:汗水铺就代码之路,三分天注定,七分靠打拼
  2. 设置Eclipse可以Debug模式调试JDK源码,并显示局部变量的值
  3. cad新手必练300图_[CAD]平面练习图,CAD新手练技术练速度的好去处
  4. 重磅!阿里巴巴开源首个边缘计算云原生项目 OpenYurt
  5. [转载] Java 将字符串首字母转为大写 - 利用ASCII码偏移
  6. 测试类什么时候初始化
  7. IMDB-WIKI人脸属性数据集解析,dob matlab序列号转为出生日期
  8. 系统安装之十 U盘安装原版win10
  9. 华为初面+综合面试(Java技术面
  10. 非线性微分方程的线性化
  11. Python 编码错误的本质原因
  12. 山东春考计算机组装与维修,山东春考计算机组装与维修模拟试题(11页)-原创力文档...
  13. Java面试题大全带答案 40道
  14. 同步器Synchronizer
  15. 百度松果 买礼物(贪心)
  16. python函数中self的作用_在Python中self的用途是什么?
  17. FDTD Solutions-边界条件
  18. 一文读懂数据仓库、数据湖、湖仓一体
  19. 计算机基础16秋在线作业,16秋华师《计算机基础》在线作业
  20. Pytorch模型加密的方法

热门文章

  1. 【认识TCP 序列和确认编号】
  2. python抛出异常 后如何接住_如何在try中捕获异常后继续循环。。。例外
  3. 自己动手写网络爬虫(第一天)
  4. 科学计算机算ph,科学计算器TechCalc v4.8.0特别高级会员版
  5. 计算机文献检索试题及答案,文献检索试题及答案.doc
  6. 汽车故障诊断技术【8】
  7. 华硕笔记本禁用触控板方法
  8. wowza拉流和推流接口备忘
  9. war压缩命令_宝塔面板linux版解压WAR文件时,如何解压的三种方法介绍
  10. 撰写商业计划书的一些误区和建议