计算机视觉基础 - 边缘和轮廓检测

深度学习以外的视觉算法

尽管深度学习为基础的计算机视觉技术攻克了很多传统算法的难题,但了解这些传统的视觉算法依然是有必要的,因为它们提供了很多不同的理解计算机视觉的视角。并且相比于深度神经网络来说,计算机视觉的很多算法学起来还是很有挑战性的,但是如果你真正花时间去攻克它们,就会发现这些算法背后如此的巧夺天工,以至于你会由衷的为这些算法的发明者的智慧发出赞叹!

Quote from www.learnopencv.com

A lot many things look difficult and mysterious. But once you take the time to deconstruct them, the mystery is replaced by mastery and that is what we are after. If you are a beginner and are finding Computer Vision hard and mysterious, just remember the following

Q : How do you eat an elephant ?
A : One bite at a time!

Spacial Coherent Data

Computer Vision 这门学科处理的信号不仅仅包含传统意义上的相机产生的照片信息,还包括其他类型的传感器产生的信息,包括声学信息,这些信息在空间之内传播和变化,因此被称为 Spacial Coherent Data,通过对这些信息进行捕捉和感知,可以让机器获得空间感知能力。CV 的几个典型的应用场景包括自动驾驶、医学影像分析、图像标注和人脸识别等等。

Cognitive and Emotional Intelligence

认知智能 Cognitive Intelligence 即通常所说的 IQ,情感智能 Emotional Intelligence 即通常所说的 EQ,通过计算机视觉技术 Affectiva 使得计算机可以识别与之交互的人类的面部表情和肢体语言,进而了解此时与之交互的人的情感状态,从而建立更加丰富的情感交互能力。

计算机视觉应用的通用工作流

对于上述表情功能的实现,以及任何一个计算机视觉任务,通常都需要经过以下几个大致的过程:

  • 输入数据的预处理:包括降噪、缩放、和变更颜色空间等,其最重要的目的在于对于多个输入图片的标准化 Standardization

  • 定位目标区域:目标检测和图像分割,例如在情感识别中,需要定位面部重要的特征点

  • 特征提取:提取特征数据,例如下文中提到的 HOG,FAST 等

  • 目标识别:对于新的输入执行目标识别和特征匹配,进而对于被检测的对象的状态给予一个预测

图像的表征

在计算机的世界里,图像通常被表示为一系列的网格状的数字(像素)矩阵,这一表示形式是大多数图像处理技术的基础。我们可以通过坐标位置来确定某个像素点的位置,并通过更改该点的像素的值来更改图像的显示样式。

色彩空间 Color Spaces

同人类理解世界一样,对于计算机来说同样有“知识的表示形式决定了学习的难易程度”,为了更好的表示图像信息,除了常用的 RGB 通道外,还有两种常用的颜色空间表示方法:

  • RGB:Red,Green,Blue,这个空间中的 RGB 分布取值范围都在 [0, 255],呈均匀分布

  • HSV: 色调 Hue,饱和度 Saturation,亮度 Value,这个空间中的颜色分布呈现为一个圆柱体,在不同的软件中这三者的取值范围不同,在使用中需要注意转换。在 OpenCV 中 Hue 通道的取值范围为 [0, 179],Saturation 通道的取值范围是 [0, 255],亮度 Value 通道的取值范围为 [0, 255]。由于色调通道在不同的光照条件下变化范围不大,而亮度通道则在不同的光照条件下变化明显,因此可以通过调整色调通道的值来更好的选择目标区域而避免光照条件的影响。

  • HLS:Hue, Lightness, Saturation

由于在大部分计算机视觉应用中,光照条件对于算法的识别能力是有影响的,因此后两种颜色空间表示方法考虑了亮度信息,因此可以用于图像的光照条件的分辨。

通过 OpenCV 将图片从 RGB 空间转到 HSV 空间的代码如下:

cv2.cvtColor(input_image, cv2.COLOR_BGR2HSV)

这里需要注意的是在 OpenCV 中默认的颜色顺序是 BGR 而非 RGB,如果需要做这一转换,则可以通过如下代码进行:

cv2.cvtColor(input_image, cv2.COLOR_BGR2RGB)

由于大部分人对于 RGB 空间的色彩更熟悉,所以如果想知道某一个 RGB 空间下的色彩对应的 HSV 的值,可以通过如下代码来实现:

green = np.uint8([[[0,255,0 ]]])
hsv_green = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
print(hsv_green)

目标区域的选取

在既定的色彩空间下,可以通过 cv2.inRange() 选择取值在一定范围内的像素来对目标区域进行选取,并且由于图像被以数组的形式进行存储,因此可以通过简单的数组加法来对多个图像进行叠加。由于纯色的背景更加容易通过这一操作来编辑,因此在 Udacity 的课程中讲者背后的背景通常都是蓝色的。

Space travelling Pizza
# Define the masked area
lower_blue = np.array([0, 0, 230]) 
upper_blue = np.array([50, 50, 255])
mask = cv2.inRange(image_copy, lower_blue, upper_blue)

# Mask the image to let the pizza show through
masked_image = np.copy(image_copy)
masked_image[mask != 0] = [0, 0, 0]

# Display it!
plt.imshow(masked_image)

边缘检测和图像信息的过滤

在图像中除了简单的 RGB 或 HSV 通道信息外,图像本身的模式变化也是一个非常有用的信息,例如我们在图像中看到的物体和背景的边缘 Edges,其最明显的特征就是边缘两侧具有明显的色彩强度 Intensity 变化

Intensity is a measure of light and dark similar to brightness, and we can use this knowledge to detect other areas or objects of interest. For example, you can often identify the edges of an object by looking at an abrupt change in intensity which happens when an image changes from a very dark to light area.

进一步地,在很多情况下图像中的模式变化是有一定规律可循的,衡量这一变化模式的一个重要指标就是变化的频率,而研究频率最为有效的办法之一则是伟大的傅立叶变换。所谓的高频图像或者图像当中的高频部分就是指其强度数值变化频率较高的部分,而低频图像或者图片中的低频部分则是指图片上信息一致或者渐变的部分。

Similar to sound, frequency in images is a rate of change. But, what does it means for an image to change? Well, images change in space, and a high frequency image is one where the intensity changes a lot. And the level of brightness changes quickly from one pixel to the next. A low frequency image may be one that is relatively uniform in brightness or changes very slowly. Most images have both high-frequency and low-frequency components.

Fourier transform for images with different frequencies

The Fourier Transform (FT) is an important image processing tool which is used to decompose an image into its frequency components. The output of an FT represents the image in the frequency domain, while the input image is the spatial domain (x, y) equivalent. In the frequency domain image, each point represents a particular frequency contained in the spatial domain image. So, for images with a lot of high-frequency components (edges, corners, and stripes), there will be a number of points in the frequency domain at high frequency values.

通过傅立叶变换将图像上的信息分解为不同的频率组成部分之后,就可以为了过滤掉不需要的信息或者突出感兴趣的信息而使用不同类型的滤波器 Filter 来对图像进行处理。

在图像处理的语境中,高通滤波器 High-pass filter 的目的在于锐化 Sharpen 图像中的高频部分,其通过类似 Sobel 滤波器(每一个滤波器只检测一个方向的边缘)这样的边缘检测卷积核来实现。而低通滤波器则主要用于降噪 Denoising 或图像模糊 Blur,其通过平均化卷积核区域内的所有像素的信息,如 Gaussian Blur 来实现。

# High pass filter further intensify the center element if it already stand out
# All elements should add up to 0
high_pass = np.array([[-1, -1, -1],
                     [-1, 8, 1],
                     [-1, -1, -1]])
high_pass
array([[-1, -1, -1],
       [-1,  8,  1],
       [-1, -1, -1]])

# Low pass filter averaging and smoothing the target area
low_pass = np.ones((3, 3)) / 9
low_pass
array([[ 0.11111111,  0.11111111,  0.11111111],
       [ 0.11111111,  0.11111111,  0.11111111],
       [ 0.11111111,  0.11111111,  0.11111111]])
High pass filter will black out low frequency part of the image

我们所讲的滤波器 Filter 在很多情况下又被称为核 kernel,通过采用特定的核和滤波器对于图像进行过滤操作,可以突出图像中感兴趣的部分而忽略不感兴趣的部分。在实际使用中,通常先将图片进行低通滤波,再进行高通滤波以避免产生无意义的干扰信息 Noise。

MRI brain image processing

A kernel is a set of weights that are applied to a region in a source image to generate a single pixel in the destination image.

对于执行边缘检测的核(高通)来说,由于需要通过计算的结果来反应图像上每一个位置的强度变化情况,因此每一个核中的数字加总的结果应该是 0,此时如果在某一个位置得到的结果为 0,则意味这在这个地区没有强度变化,也即没有边缘过渡。核中的参数加总不为 0 的操作会对图像进行加亮或暗化。由计算过程可知,执行边缘检测的过滤计算的结果的大小即代表了核覆盖的区域内的边缘过渡是否强烈,结果越大则边缘越明显,而核中的数字也称为权重的原因正是其决定了相应位置的像素在最终计算结果中的权重值。

Canny Edge Detection

在边缘检测过程中,常见的两个问题是:

  • 多大程度的强度改变应该被定义为一个边缘?

  • 我们如何能够一致性的展示出细微和宽厚的边缘?

这就要求我们能够拥有更加高性能的边缘检测器 - Canny Edge Detection,其实现过程如下:

  • 通过 Gaussian Blur 来完成低通滤波

  • 通过多个 Sobel 滤波器来识别出边缘的粗细和方向

  • 采用非极大抑制来分离出较粗的线条,并将它们以一个像素的宽度来重新展示,经过处理后的图像是一个 binary image

  • Use Hysteresis to isolate the best edges,在选择的过程中需要设定一个强度梯度的最小和最大的阈值,并且只保留处于最低阈值之上的与其他确定边缘连接的线条,以保留有意义的边界信息并去掉可能的噪声,其中阈值最小和最大的值的比例一般选择 1:2 或 1:3

How Hysteresis works
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

lower = 100
upper = 300

edges = cv2.Canny(gray, lower, upper)
plt.imshow(edges, cmap='gray')

在建立的基本的边缘检测工具后,下一步就是要了解如果进一步地在图像中检测特定的形状。

Hough Transform

Hough Transform 是一个非常重要的形状检测工具,这个方法的实现原理比较复杂,尝试理解如下:

在直角坐标系下的一条直线可以被表示为 y = mx + b,相应的这条直线在极坐标系下则可以用一个点(ρ, θ)来表示,其中 ρ 代表原点到这条直线的距离,而 θ 则表示这条直线和 x 轴的夹角。并且对于指定的点 (x, y),如果再指定直线与坐标轴的夹角 θ,则 ρ 可以通过 ρ = xcosθ + ysinθ 来唯一确定。由于在直角坐标系下通过直线上某个点可以绘制出无数条直线,这些直线所对应的极坐标下的点则构成一条正弦曲线。在实际检测过程中,算法将建立一个二维的数组,称作 Accumulator,其横轴为 ρ ,纵轴为 θ,对于任意一个点 (x, y) 和它相邻的点,算法会计算是否在这个点附近有直线存在。

在实际使用中,在执行 Hough 变换之前需要将图像转化为灰度图像,并通过 Canny 边缘检测器进行滤波,函数的输入结果为 binary image。

gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

# Define our parameters for Canny
low_threshold = 50
high_threshold = 100
edges = cv2.Canny(gray, low_threshold, high_threshold)

# Parameters for Hough Transform
rho = 1
theta = np.pi/180
threshold = 60
min_line_length = 100
max_line_gap = 5

# Creating an image copy to draw lines on
line_image = np.copy(image) 

# Run Hough on the edge-detected image
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                        min_line_length, max_line_gap)

# Iterate over the output "lines" and draw lines on the image copy
for line in lines:
    for x1,y1,x2,y2 in line:
        cv2.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5)
        
plt.imshow(line_image)
Square detection with Hough Transformation

Haar Cascade Face Detection

Haar Cascade 算法通过一系列包含和不包含脸部的图片来完成训练,这个利用 Haar feature 进行脸部识别的算法在计算的过程中会逐步消除掉判断为非脸部的图片位置,进而减小检测范围,因此算法性能十分高效,可以实时进行面部检测。

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  

# load in cascade classifier
face_cascade = cv2.CascadeClassifier('detector_architectures/haarcascade_frontalface_default.xml')

# run the detector on the grayscale image
faces = face_cascade.detectMultiScale(gray, 4, 6)

img_with_detections = np.copy(image)   # make a copy of the original image to plot rectangle detections ontop of

# loop over our detections and draw their corresponding boxes on top of our original image
for (x, y, w, h) in faces:
    # draw next detection as a red rectangle on top of the original image.  
    # Note: the fourth element (255,0,0) determines the color of the rectangle, 
    # and the final argument (here set to 5) determines the width of the drawn rectangle
    cv2.rectangle(img_with_detections, (x, y), (x+w, y+h), (255, 0, 0), 5)  

Corner Detection

在很多情况下,图像中的成角部分能够提供很多特别有效的特征信息,在 OpenCV 中一个较为常用的方法是 cv2.cornerHarris

gray = cv2.cvtColor(image_copy, cv2.COLOR_RGB2GRAY)
gray = np.float32(gray)

# Detect corners 
dst = cv2.cornerHarris(gray, 2, 3, 0.04)

# Dilate corner image to enhance corner points
dst = cv2.dilate(dst, None)

# Try changing this free parameter, 0.1, to be larger or smaller ans see what happens
thresh = 0.1*dst.max()

# Create an image copy to draw corners on
corner_image = np.copy(image_copy)

# Iterate through all the corners and draw them on the image (if they pass the threshold)
for j in range(0, dst.shape[0]):
    for i in range(0, dst.shape[1]):
        if(dst[j,i] > thresh):
            # image, center pt, radius, color, thickness
            cv2.circle( corner_image, (i, j), 1, (0, 255, 0), 1)

plt.imshow(corner_image)
Harris Corner Detection

Image Segmentation

在完成了前述一系列的基本特征检测以后,一个重要的应用就是对图像进行分割,在图像分割之前需要进一步完成的是轮廓提取(在 OpenCV 中当背景是黑色,而物体是白色时最容易实现轮廓提取,这就是为何很多处理算法的结果都是 binary image)。

# Convert to grayscale
gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)

# Create a binary thresholded image
retval, binary = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)

# Find contours from thresholded, binary image
retval, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Draw all contours on a copy of the original image
contours_image = np.copy(image)
contours_image = cv2.drawContours(contours_image, contours, -1, (0,255,0), 3)

plt.imshow(contours_image)
findContours and drawContours with openCV

参考阅读

  1. An Intuitive Explanation of Convolutional Neural Networks

  2. Fourier transforms of images

  3. 伟大的马老师讲解的傅立叶变换

  4. 傅里叶分析之掐死教程

  5. Hough Tranform

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

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,294评论 0 10
  • The Inner Game of Tennis W Timothy Gallwey Jonathan Cape ...
    网事_79a3阅读 11,737评论 2 19
  • 此时此刻的我坐着凳子,思忖着…… 一个月前,自己的大学好友喊我去西安!自己蛮迟疑的!自己在深圳处在收入中断,又无法...
    谢聃阅读 181评论 0 0
  • 从三年级开始的千万字阅读中,我认识了不少作家,比如杨红樱、郁雨君、伍美珍、曹文轩等、我最喜欢曹文轩的《草...
    牡丹花_8f9a阅读 350评论 0 0
  • 一世倾心,入眼痴迷,三千弱水,只取一瓢,苦中乐盼,文中寄情,日思倩影,夜想美颜,三月期限,验我心坚,相见恨晚,能付...
    将门阅读 252评论 0 0