使用Python+opencv进行图像处理(一) | 视觉入门

计算机视觉是人工智能最热门的应用领域之一。人工智能技术推动了汽车自动驾驶、机器人以及各种照片处理类软件的巨大发展。目标检测技术也在稳步推进。生成对抗网络(GANs)同样也是人们最近比较关注的一个问题。这些都在向我们展示未来计算机视觉领域的发展前景是多么的不可限量。


让我们一起登上人工智能发展的高速列车。从本文开始,我们将有一系列关于图像处理和目标检测基础知识的教程。本篇是OpenCV入门教程第一部分,完整的系列教程如下:

1. 理解颜色模型与在图像上绘制图形(图像处理基本操作)。

2. 基本的图像处理与过滤。

3. 从特征检测到人脸检测(TBU


本系列的第一部分将从Opencv的安装,结合代码实战讲解颜色模型与图形绘制讲起。本教程的完整代码已经放在Github上,方便大家使用。


一、OpenCV简介

图像处理是指对图像执行一些操作以达到预期效果的过程。可以类比数据分析工作,在数据分析时我们需要做一些数据预处和特征工程。图像处理也是一样的。我们通过图像处理来处理图片从而可以从中提取处一些更加有用的特征。我们可以通过图像处理减少图像噪声调整图像亮度、颜色或者对比度等等。想要进一步系统了解图像处理基础知识,参看https://www.youtube.com/watch?v=QMLbTEQJCaI)。

OpenCV是Open Source Computer Vision的缩写,由英特尔公司于1999年推出。它最初是用C/ C++编写的,所以你可能会看到更多用C语言而不是Python编写的教程。但现在它在Python中也被广泛用于计算机视觉。首先,让我们为使用OpenCV配置环境。安装过程如下,详细安装描述参看https://pypi.org/project/opencv-python/

pip install opencv-python==3.4.2
pip install opencv-contrib-python==3.3.1


安装完成后,可以通过下方两条命令测试其是否正常工作。如果没有任何报错,那么就可以开始使用了!

import cv2
cv2.__version__


我们使用用OpenCV做的第一步就是导入一个图像,如下方所示。

import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline

# Import the image
img = cv2.imread('burano.jpg')

plt.imshow(img)


上图是在意大利最美丽的岛屿之一布拉诺所拍摄的。如果你去过这个地方,你可能会注意到这幅图里有些不同。这确实和我们通常看到的布拉诺的照片有点不同。这是因为OpenCV中颜色模式的默认设置顺序是BGR,不同与Matplotlib。因此,要在RGB模式下查看图像,我们需要将它从BGR转换为RGB,如下所示。

# Convert the image into RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

plt.imshow(img_rgb)


这张图就是真正的布拉诺了,多么美丽!


二、不只是RGB

我们再来谈谈颜色模型。颜色模型是一个使用原色构建全系列颜色的系统。这里先介绍这两种不同的颜色模型:“加色模型”和”减色模型”。加色模型使用光代表计算机屏幕上的颜色,而减色模型使用墨水在纸上打印这些数字图像。前者的原色由红色、绿色和蓝色(RGB)组成,后者有蓝色、品红、黄色和黑色(CMYK)四种原色组成。我们在图像上看到的所有其他颜色都是由这些原色组合或混合而成的。所以当分别用RGB、CMYK表示一张图像时,图像可以有着略微不同地表达。如下图所示。


日常生活中见到最多的就是这两种颜色模型。然而,在彩色模型的世界里不仅仅只有这两种颜色模型。众多的颜色模型中,灰度(grayscale)、HSV和HLS也是你会在计算机视觉任务中经常看到的。


灰度(grayscale)很简单。它通过黑白的强度来表示图像和形态,这也意味着它只有一个通道。要查看灰度图像,我们需要将颜色模型转换为灰色,就像前面对BGR图像所做的操作那样。

# Convert the image into gray scale
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

plt.imshow(img_gray, cmap = 'gray')


实际上,RGB图像是由三个通道叠加而成的:R, G, b。所以如果我们把每个通道一个一个的描绘出来,我们就可以理解颜色通道是如何构成的了!

# Plot the three channels of the image
fig, axs = plt.subplots(nrows = 1, ncols = 3,figsize = (20, 20))

for i in range(0, 3):
ax =axs[i]
ax.imshow(img_rgb[:, :, i], cmap = 'gray')

plt.show()


观察上面的图片。这三幅图像展示了每个通道是如何组成的。在R通道图中,红色饱和度高的部分看起来是白色的。这是由于红色部分中的值接近255。在灰度模式下,值越高颜色就越白。你还可以使用G或B通道来检查这一点,并比较某些部分之间的差异。

HSV和HLS有一些不同。正如在上图看到的那样,他们有一个三维的表达,更类似于人类的感知方式。HSV代表色调、饱和度和色值。HSL代表色调、饱和度和亮度。HSV的中轴是色值,HSL的中轴是光量。沿着中心轴的角度,有色调和实际的颜色。与中心轴的距离属于饱和度。转换颜色模型的方法如下。

# Transform the image into HSV and HLS models
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)

# Plot the converted images
fig, (ax1, ax2) = plt.subplots(nrows = 1, ncols =2, figsize = (20, 20))
ax1.imshow(img_hsv)
ax2.imshow(img_hls)

plt.show()


但是为什么要变换颜色?这些有什么用处?一个很典型的例子--车道检测。观察下图,不同颜色模式下的车道线。在计算机视觉任务中,我们利用掩膜(masking)进行多色模式转换。如果你想了解更多关于图像处理在车道检测任务中的应用,可参看这篇文章。

(https://towardsdatascience.com/finding-lane-lines-simple-pipeline-for-lane-detection-d02b62e7572b

图像处理是就是对图像数据进行预处理。它可以减少噪音,提取有用的颜色模型,从而简化分类和检测任务。因此,所有上述技术,包括我们稍后将讨论的技术,都是为了帮助模型更容易地实现检测。


三、在图像上绘制图形

让我们在图像上添加一些图形。咱们这次代码示例中使用的图片来自巴黎“爱的墙”。上面用各种国际语言写满了“我爱你”。我们要做的是找到语言中的单词并用矩形标记它们。假如我们要定位韩语版本“我爱你”。首先,复制原始图像并用cv2.rectangle()函数绘制一个矩形,同时给出左上角和右下角的坐标值。如下:

# Copy the image
img_copy = img.copy()

# Draw a rectangle
cv2.rectangle(img_copy, pt1 = (800, 470), pt2 =(980, 530),
color = (255, 0, 0), thickness = 5)

plt.imshow(img_copy)


使用cv2.circle()函数,画一个圆,圈出更多的韩语单词。我们需要指定它的圆心的点和半径的长度。

# Draw a circle
cv2.circle(img_copy, center = (950, 50), radius =50,
color = (0, 0, 255), thickness = 5)

plt.imshow(img_copy),


我们还可以将文本数据放在图像上。使用cv2.putText()函数,我们可以指定文本的位置、字体样式和大小。

# Add text
cv2.putText(img_copy, text = "the Wall ofLove",
org = (250, 250),
fontFace = cv2.FONT_HERSHEY_DUPLEX,
fontScale = 2,
color = (0, 255, 0),
thickness = 2,
lineType = cv2.LINE_AA)

plt.imshow(img_copy)


四、不止是图像

在上边的介绍中,我们选取意大利和法国的两张风景图作为示例。假如,我们想要画张地图把这些地方标出来。那么,首先我们要创建一个窗口并绘制图形。不一样的是这里不是通过指定点绘制图形而是通过点击响应。先试试绘制圆圈。首先创建一个函数,它将用位置和鼠标点击的数据绘制一个圆圈。

# Step 1. Define callback function
def draw_circle(event, x, y, flags, param):
ifevent == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img, center = (x, y), radius = 5,
color = (87, 184, 237), thickness = -1)

elifevent == cv2.EVENT_RBUTTONDOWN:
cv2.circle(img, center = (x, y), radius = 10,
color = (87, 184, 237),thickness = 1)


当按下鼠标点击按钮时,使用cv2.EVENT_LBUTTONDOWN或

cv2.EVENT_RBUTTONDOWN记录位置数据。把鼠标的位置设置为圆心(x, y),并绘制圆圈。

# Step 2. Call the window
img = cv2.imread('map.png')

cv2.namedWindow(winname = 'my_drawing')
cv2.setMouseCallback('my_drawing', draw_circle)


设置一个地图作为窗口的背景,并将窗口命名为my_drawing。使用cv2.setMouseCallback()函数,在窗口和我们在步骤1中创建的函数draw_circle之间建立了一个连接。

# Step 3. Execution
while True:
cv2.imshow('my_drawing',img)
ifcv2.waitKey(10) & 0xFF == 27:
break

cv2.destroyAllWindows()


现在我们使用while循环执行窗口。if子句的执行条件是,当我们按下键盘上的ESC时,将窗口设置为关闭。


接下来尝试绘制一个矩形。由于在cv2.rectangle()函数中,矩形需要两个点来表示pt1和pt2,所以我们需要一个额外的步骤来设置第一个点击点为pt1,最后一个点击点为pt2。我们要用cv2.EVENT_MOUSEMOVE和cv2.EVENT_LBUTTONUP来检测鼠标的移动。


我们首先将drawing = False定义为默认值。当按下左键时,绘图变为true,我们将第一个位置设为pt1。如果正在绘图,它将以当前点为pt2,并在移动鼠标时继续绘制矩形。就像数字重叠一样。当左键打开时,绘图变为false,它将鼠标的最后一个位置作为pt2的最后一个点。

# Initialization
drawing = False
ix = -1
iy = -1

# create a drawing function
def draw_rectangle(event, x, y, flags, params):

globalix, iy, drawing
ifevent == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x, y

elifevent == cv2.EVENT_MOUSEMOVE:
ifdrawing == True:
cv2.rectangle(img, pt1=(ix, iy), pt2=(x, y),
color = (87, 184,237), thickness = -1)

elifevent == cv2.EVENT_LBUTTONUP:
drawing = False
cv2.rectangle(img, pt1=(ix, iy), pt2=(x, y),
color = (87, 184, 237),thickness = -1)


在步骤1中将draw_circle函数替换为draw_rectangle。请不要忘记在回调函数cv2.setMouseCallback()中进行更改。因此,整个代码脚本将如下所示。

import cv2
import numpy as np

# Step 1. Define callback function
drawing = False
ix = -1
iy = -1

def draw_rectangle(event, x, y, flags, params):

globalix, iy, drawing

ifevent == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x, y

elifevent == cv2.EVENT_MOUSEMOVE:
ifdrawing == True:
cv2.rectangle(img, pt1 = (ix, iy), pt2 = (x, y),
color = (87, 184,237), thickness = -1)

elifevent == cv2.EVENT_LBUTTONUP:
drawing = False
cv2.rectangle(img, pt1 = (ix, iy), pt2 = (x, y),
color = (87, 184, 237),thickness = -1)


# Step 2. Call the window
img = cv2.imread('map.png')

cv2.namedWindow(winname = 'my_drawing')
cv2.setMouseCallback('my_drawing',draw_rectangle)

# Step 3. Execution
while True:
cv2.imshow('my_drawing', img)
ifcv2.waitKey(10) & 0xFF == 27:
break

cv2.destroyAllWindows()

五、总结与展望

本篇文章介绍了Opencv的安装、图像颜色模型的转换与图形绘制。下次,将介绍图像轮廓提出与目标检测等技术。敬请期待!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容