python opencv读取图像并生成plt文件
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文件相关推荐
- python opencv读取图像像素值_python-opencv--图像像素通道读取及修改
data/dtype/size/shape/len ''' import cv2 as cv import numpy as np def access_pixes(image): print(ima ...
- python opencv 读取图像检查是否为空
通过 is None 检查即可 示例代码: import cv2img = cv2.imread(your_path) if img is None:print("your image is ...
- python图片保存jpg、show变成bmp_Python 实现判断图片格式并转换,将转换的图像存到生成的文件夹中...
Python 实现判断图片格式并转换,将转换的图像存到生成的文件夹中 我就废话不多说了,直接上代码吧! import Image from datetime import datetime impor ...
- 使用OpenCV python模块读取图像并将其另存为灰度系统
In Python, we can use an OpenCV library named cv2. Python does not come with cv2, so we need to inst ...
- Python OpenCV 之图像乘除与像素的逻辑运算,图像处理取经之旅第 17 天
今天的学习的内容是:通过 Python OpenCV 对图像实现乘除操作,涉及函数为 cv2.multiply 与 cv2.divide.后面又补充了一些像素的逻辑运算,以及一个综合案例 cv2.mu ...
- 【Python OpenCV】图像直方图 calcHist方法 equalizeHist方法
[Python OpenCV]图像直方图 calcHist方法 equalizeHist方法 (一)图像直方图 图像的构成是有像素点构成的,每个像素点的值代表着该点的颜色(灰度图或者彩色图).所谓直方 ...
- Python+OpenCV:图像修复(Image Inpainting)
Python+OpenCV:图像修复(Image Inpainting) 理论 Most of you will have some old degraded photos at your home ...
- Python+OpenCV:图像二进制鲁棒独立基本特征(BRIEF, Binary Robust Independent Elementary Features)
Python+OpenCV:图像二进制鲁棒独立基本特征(BRIEF, Binary Robust Independent Elementary Features) 理论 We know SIFT us ...
- Python+OpenCV:图像快速角点检测算法(FAST Algorithm for Corner Detection)
Python+OpenCV:图像快速角点检测算法(FAST Algorithm for Corner Detection) 理论 Feature Detection using FAST Select ...
- Python+OpenCV:图像Shi-Tomasi角点检测器
Python+OpenCV:图像Shi-Tomasi角点检测器 理论 The scoring function in Harris Corner Detector was given by: Inst ...
最新文章
- 胡阳:汗水铺就代码之路,三分天注定,七分靠打拼
- 设置Eclipse可以Debug模式调试JDK源码,并显示局部变量的值
- cad新手必练300图_[CAD]平面练习图,CAD新手练技术练速度的好去处
- 重磅!阿里巴巴开源首个边缘计算云原生项目 OpenYurt
- [转载] Java 将字符串首字母转为大写 - 利用ASCII码偏移
- 测试类什么时候初始化
- IMDB-WIKI人脸属性数据集解析,dob matlab序列号转为出生日期
- 系统安装之十 U盘安装原版win10
- 华为初面+综合面试(Java技术面
- 非线性微分方程的线性化
- Python 编码错误的本质原因
- 山东春考计算机组装与维修,山东春考计算机组装与维修模拟试题(11页)-原创力文档...
- Java面试题大全带答案 40道
- 同步器Synchronizer
- 百度松果 买礼物(贪心)
- python函数中self的作用_在Python中self的用途是什么?
- FDTD Solutions-边界条件
- 一文读懂数据仓库、数据湖、湖仓一体
- 计算机基础16秋在线作业,16秋华师《计算机基础》在线作业
- Pytorch模型加密的方法