利用python的海龟素描出一张图片的内容

摘要: 这是一篇有关利用OpenCV+turtle根据图片来绘画出一副素描画的总结。

(拖延了好久!很抱歉@_@)

其实这是我在做车牌号识别的项目中玩OpenCV产生的灵感!想着turtle竟然可以读取坐标进行画图,那我将图片的坐标让海龟自动帮我画画岂不是很好吗!哈哈哈哈哈哈哈我可真是个小天才,老资本家了(狗头)

视频效果展示:

图片效果展示:

变成

2313S1

tip: 根据[名为一无]小伙伴提出的建议做出说明:新版cv2这个库的findContours这个函数对比老版本返回值只有两个,另外优化上turtle设置成速度为0就已经是最大速度了,更快可以增加一个delay(0)以去掉绘画延迟

步骤拆解

边缘检测

1
2
image = cv2.imread(pic,0)			#读取图像
edges = cv2.Canny(image,100,200) #进行边缘检测

在 OpenCV 中只需要一个函数:cv2.Canny(),就可以完成。 让我们看如何使用这个函数。这个函数的第一个参数是输入图像。第二和第三 个分别是 minVal 和 maxVal。第三个参数设置用来计算图像梯度的 Sobel 卷积核的大小,默认值为 3。最后一个参数是 L2gradient,它可以用来设定 求梯度大小的方程。当为False时 使用方程:Edge−Gradient(G) = |G2 x | + |G2 y | 代替,默认值为 False。

效果图:image-20200816120840476

很棒提取出来的边缘很不错的,各位可以试着调节minVal 和 maxVal来看看有什么区别!

灰度处理

1
img_gray = cv2.cvtColor(edges,cv2.COLOR_BAYER_BG2GRAY)

在 OpenCV 中有超过 150 中进行颜色空间转换的方法。但是你以后就会 发现我们经常用到的也就两种:BGR↔Gray 和 BGR↔HSV。 我们要用到的函数是:cv2.cvtColor(input_image,flag),其中 flag 就是转换类型。 对于 BGR↔Gray 的转换,我们要使用的 flag 就是 cv2.COLOR_BGR2GRAY。 同样对于 BGR↔HSV 的转换,我们用的 flag 就是 cv2.COLOR_BGR2HSV。

为什么已经变成黑白了还要进行灰度处理呢,这是为了更好的进行下一步(图像阈值)做准备,可以先来看看不进行灰度处理的效果:

image-20200816121319592

点分布的不均匀,提取出来的点很少,增加灰度处理可以使得下一步图像阈值更好的进行处理。

图像阈值

1
ret, thresh = cv2.threshold(img_gray,127,255,0)

与名字一样,这种方法非常简单。但像素值高于阈值时,我们给这个像素 赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。 这个函数就是 cv2.threshhold()。这个函数的第一个参数就是原图像,原图 像应该是灰度图。第二个参数就是用来对像素值进行分类的阈值。第三个参数 就是当像素值高于(有时是小于)阈值时应该被赋予的新的像素值。OpenCV 提供了多种不同的阈值方法,这是有第四个参数来决定的。这些方法包括:

• cv2.THRESH_BINARY

• cv2.THRESH_BINARY_INV

• cv2.THRESH_TRUNC

• cv2.THRESH_TOZERO

• cv2.THRESH_TOZERO_INV

这一步是为了提取出thresh为下一步提取坐标做准备,此处参数部分我也不是很懂,希望小伙伴可以帮我解答

提取目标轮廓坐标

1
2
3
#下面这句是老版本opencv的写法,如果运行报错可以将下句注释,取消下下句的注释
image,contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

函数 cv2.findContours() 有三个参数,第一个是输入图像,第二个是 轮廓检索模式,第三个是轮廓近似方法。返回值有三个,第一个是图像,第二个 是轮廓,第三个是(轮廓的)层析结构。轮廓(第二个返回值)是一个 Python 列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个 Numpy 数组,包 含对象边界点(x,y)的坐标。

从这里就可以发现,目标轮廓就在我们得到的contours里啦!

打印出contours会发现里面是很多的直矩阵的坐标:

image-20200816122814951

接下来我们只要提取出坐标就大功告成了!

1
x,y,w,h = cv2.boundingRect(contours[i])

直边界矩形 一个直矩形(就是没有旋转的矩形)。它不会考虑对象是否旋转。 所以边界矩形的面积不是最小的。可以使用函数 cv2.boundingRect() 查找得到。 (x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高。

小海龟该怎么写

其实turtle怎么写,多玩几遍就知道了,说实话我也经常忘记,但是我记得有这些步骤,我去看看语法怎么写就容易写出来(记性老差了)

1
2
3
4
5
6
7
8
9
10
11
12
13
def draw(xy):
turtle.pensize(2) #设置画笔大小
turtle.setup(width=0.6,height=1.0) #设置画布大小
turtle.speed(0) #设置速度
for array in xy[::-1]:
turtle.penup() #提起画笔
turtle.goto((array[0]/2)-100,-(array[1]/2)+250) #到指定坐标
turtle.pendown() #放下画笔
turtle.forward(1) #画多长长度
print(turtle.pos()) #命令行显示出来,可以注释掉


turtle.done() #保留画的图

整体代码

下面是整体代码的实现

github:https://github.com/hongcyu/turtle_draw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import cv2
import numpy as np
import turtle

def array_pic(pic):
image = cv2.imread(pic,0)
edges = cv2.Canny(image,100,200)
img_gray = cv2.cvtColor(edges,cv2.COLOR_BAYER_BG2GRAY)
ret, thresh = cv2.threshold(img_gray,127,255,0)
#下面这句是老版本opencv的写法,如果运行报错可以将下句注释,取消下下句的注释
image,contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
xy = []
for i in range(0,len(contours)):
x,y,w,h = cv2.boundingRect(contours[i])
list_xy = [x,y]
xy.append(list_xy)
return xy
# file = open("array.txt", mode="x")
# file.writelines(str(contours))
# file.close()
# cv2.imshow("1.jpg",image)
# cv2.waitKey(0)

def draw(xy):
turtle.pensize(2)
turtle.setup(width=0.6,height=1.0)
turtle.speed(0)
for array in xy[::-1]:
turtle.penup()
turtle.goto((array[0]/2)-100,-(array[1]/2)+250)
turtle.pendown()
turtle.forward(1)
print(turtle.pos())


turtle.done()


if __name__ == "__main__":
pic = "image.jpg"
xy = array_pic(pic)
draw(xy)

注意事项:

  1. 你需要安装opencv,在cmd中输入:pip3 install opencv-python

  2. 要使用需要将图片放置在和py文件同一个文件夹下,接着修改下面的pic后的参数为图片名字。

    1
    2
    3
    4
    if __name__ == "__main__":
    pic = "image.jpg"
    xy = array_pic(pic)
    draw(xy)
------- 本文结束  感谢您的阅读 -------