频率:
对于声音,频率实际上是指声波振荡的速度
高低频率
高频图像是强度变化很大的图像。并且亮度级别从一个像素到下一个像素快速变化。低频图像可以是亮度相对均匀或变化非常慢的图像。这是一个例子中最容易看到的。
大多数图像都有高频和低频成分。在上图中,在围巾和条纹衬衫上,我们有一个高频图像模式; 这部分从一个亮度变化到另一个亮度。在同一图像中,我们看到天空和背景的部分变化非常缓慢,这被认为是平滑的低频模式。
高频分量也对应于图像中对象的边缘,这可以帮助我们对这些对象进行分类。
傅里叶变换
傅里叶变换(FT)是一种重要的图像处理工具,用于将图像分解为其频率分量。FT的输出表示频域中的图像,而输入图像是空间域(x,y)等效物。在频域图像中,每个点表示包含在空间域图像中的特定频率。因此,对于具有大量高频分量(边缘,角落和条纹)的图像,频域中将存在高频率值的多个点。
足球运动员的图像和相应的频域图像(右)。频域图像中心的集中点意味着该图像具有许多低频(平滑背景)分量
在这里,看看如何使用OpenCV完成FT 。
傅里叶变化练习
原图如左下所示,经过傅里叶变换后,图像更加接近D
高通滤波器
在图像处理中,我们用过滤器来过滤掉图像中不需要或无关的信息,也用过滤器来放大某些特征,比如物体边界或其他显著特征。
高通滤波器用于锐化图像以及强化图像的高频区域,也就是相邻像素发生突变的区域,比如从极暗过度到极亮的那些区域,由于我们观察的是强度模式,所以用过滤器来处理灰度图像(灰度图像会以简单的格式将明暗情况展示出来,从而体现强度)
以熊猫图片为例:原图那些强度没有变化或变化不大的区域,原图那些强度没有变化或变化不大的区域,会被高通过滤器过滤掉 而且像素颜色会变为黑色;但在这些区域里 由于像素比邻近像素明亮许多,但在这些区域里 由于像素比邻近像素明亮许多,结果就是把边缘强化了。(所谓边缘 就是指图像里强度发生突变的区域,通常暗示着物体边界的存在)
这类过滤器的具体原理,过滤器是以矩阵形式存在的,通常称为卷积核。
以检测边缘的高通过滤器为例,这是个三乘三的核 其元素总和为 0,边缘检测时 所有元素总和为 0 是很重要的。因为这类过滤器要计算的是相邻像素的差异 或者说变化,要计算差异 就需要将像素值相减。在这个例子中 我们要将中间像素与周围像素的值相减,如果这些核值加起来不等于 0,那就意味着计算出来的差,权重会有正负,结果就是滤波后的图像亮度会相应地提高或或降低
该内核找到围绕给定像素的顶部边缘和底部边缘之间的差异
这里我们使用opencv filter2D来创建
Sobel滤波器
Sobel滤波器非常常用于边缘检测和在图像中查找强度模式
低通滤波
噪声通常就是图像的斑点或变色部分不含任何有用信息甚至会影响处理操作,比如在边缘检测时 如果没有先解决噪声 高通过滤器就会把噪声强化。
低通过滤器是噪声最常见的解决方式,这类过滤器能阻挡特定高频部分,有效模糊图像或使图像平滑起来,从而减少高频噪声,这种过滤器的实用性在医学影像里淋漓尽致地体现了出来
要降噪 我们可以取相邻像素的均值,从而避免强度突变 特别是小范围突变,而这种取空间像素均值的做法,同应用低通过滤器来过滤高频噪声是一样的。
我们来看一个例子 用普通核来降噪:第一个也是最简单的一个 均值过滤器,会赋予中心像素及其相邻像素一样的权重,低通过滤器通常会取均值 不像高通过滤器取的是差值,因此低通过滤器的元素加起来应该为 1。这就能保留图像的亮度,但我们可以看到 这个核的元素加起来等于 9,所以我们需要进行归一化处理 也就是将核值总和除以 9
如果对整张图像的所有像素进行同样的均值处理,我们就能使图像变得平滑 图像里的强度突变也会变少,这有利于减少噪声,或使处于一定强度范围的背景区域看起来更加平滑。实际上 这类过滤器在 Photoshop 里也有应用,可对图像的某一部分进行柔化或模糊处理。
高斯模糊
除了全均值过滤器,有时我们会想要个既能模糊图像 又能更好地保存图像边缘的过滤器,为此 我们可用高斯模糊。这或许是计算机视觉应用中最常用的低通过滤器了。低通过滤.器其实就是加权平均法 赋予中心像素最大的权重.
在进行滤波处理之前,首先要将图像转换为灰度图.
Import resources and display image
import numpy as np
import matplotlib.pyplot as plt
import cv2
%matplotlib inline
# Read in the image
image = cv2.imread('images/brain_MR.jpg')
# Make a copy of the image
image_copy = np.copy(image)
# Change color to RGB (from BGR)
image_copy = cv2.cvtColor(image_copy, cv2.COLOR_BGR2RGB)
plt.imshow(image_copy)
#### 怎么确定滤波器低通或者高通
# Convert to grayscale for filtering
gray = cv2.cvtColor(image_copy, cv2.COLOR_RGB2GRAY)
# Create a Gaussian blurred image
gray_blur = cv2.GaussianBlur(gray, (9, 9), 0) ### 0 表示标准差为0
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(20,10))
ax1.set_title('original gray')
ax1.imshow(gray, cmap='gray')
ax2.set_title('blurred image')
ax2.imshow(gray_blur, cmap='gray')
Test performance with a high-pass filter
# High-pass filter
# 3x3 sobel filters for edge detection
sobel_x = np.array([[ -1, 0, 1],
[ -2, 0, 2],
[ -1, 0, 1]])
sobel_y = np.array([[ -1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]])
# Filter the orginal and blurred grayscale images using filter2D
filtered = cv2.filter2D(gray, -1, sobel_x)
filtered_blurred = cv2.filter2D(gray_blur, -1, sobel_x)
filtered_blurred_y = cv2.filter2D(gray_blur, -1, sobel_y)
f, (ax1, ax2,ax3) = plt.subplots(1, 3, figsize=(20,10))
ax1.set_title('original gray')
ax1.imshow(filtered, cmap='gray')
ax2.set_title('blurred image_x')
ax2.imshow(filtered_blurred, cmap='gray')
ax3.set_title('blurred image_y')
ax3.imshow(filtered_blurred_y, cmap='gray')
# Create threshold that sets all the filtered pixels to white
# Above a certain threshold
retval, binary_image = cv2.threshold(filtered_blurred, 50, 255, cv2.THRESH_BINARY)
plt.imshow(binary_image, cmap='gray')
import numpy as np
import matplotlib.pyplot as plt
import cv2
%matplotlib inline
# Define gaussian, sobel, and laplacian (edge) filters
gaussian = (1/9)*np.array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]])
sobel_x= np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
sobel_y= np.array([[-1,-2,-1],
[0, 0, 0],
[1, 2, 1]])
# laplacian, edge filter
laplacian=np.array([[0, 1, 0],
[1,-4, 1],
[0, 1, 0]])
filters = [gaussian, sobel_x, sobel_y, laplacian]
filter_name = ['gaussian','sobel_x', \
'sobel_y', 'laplacian']
# perform a fast fourier transform on each filter
# and create a scaled, frequency transform image
f_filters = [np.fft.fft2(x) for x in filters]
fshift = [np.fft.fftshift(y) for y in f_filters] # 1、在matlab中,经过fft变换后,数据的频率范围是从[0,fs]排列的。
#2、而一般,我们在画图或者讨论的时候,是从[-fs/2,fs/2]的范围进行分析。
#3、因此,需要将经过fft变换后的图像的[fs/2,fs]部分移动到[-fs/2,0]这个范围内。
frequency_tx = [np.log(np.abs(z)+1) for z in fshift]
# display 4 filters
for i in range(len(filters)):
plt.subplot(2,2,i+1),plt.imshow(frequency_tx[i],cmap = 'gray')
plt.title(filter_name[i]), plt.xticks([]), plt.yticks([])
plt.show()
白色或浅灰色的区域,允许那部分频谱通过!黑色区域意味着部分光谱被遮挡在图像之外。
频谱中的低频在频变换图像的中心,高频在边缘。你应该看到高斯滤波器只允许低通频率通过,这是频率变换图像的中心。sobel滤波器会屏蔽某个方向的频率,而拉普拉斯滤波器(所有边缘,不管方向如何)会屏蔽低频!
Canny 边缘检测器
要有准确的边缘检测效果 结合使用低通和高通过滤器有多重要。
CANNY是最好用也是最常用的边缘检测器之一,因为该检测器会借助一系列操作 不断生成精准的检测边缘。
首先 检测器用高斯模糊过滤掉噪声
2.然后用 Sobel 过滤器确定图像边缘的强度和方向
3.接着 借助 Sobel 过滤器的输出,Canny 会用非极大抑制,来观察每个检测边缘的强度和方向,选出局部最大像素,从而把最强的边缘绘制成连续的、一个像素宽的细线
4.最后 用滞后阀值来分离最佳边缘,滞后阀值是双阀值化操作,以某图一像素宽的横切面为例:这里的曲线代表边缘强度,峰值指的是十分强的边缘,使用滞后阀值时我们要确定一个高阀值以便允许这些强边缘通过;再设置一个低阀值,何低于该阀值的边缘即为弱边缘会被舍弃,但位于高低阀值之间的边缘,只有当其与另一个强边缘相连时 才会得到保留.
由于 Canny 着重强调重要边缘,所以它特别适合检测边界和形状。
例子 canny
import numpy as np
import matplotlib.pyplot as plt
import cv2
%matplotlib inline
# Read in the image
image = cv2.imread('images/brain_MR.jpg')
# Change color to RGB (from BGR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
将图片转为灰度图片
# Convert the image to grayscale for processing
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
plt.imshow(gray, cmap='gray')
实现canny边缘检测
# Try Canny using "wide" and "tight" thresholds
wide = cv2.Canny(gray, 30, 100)#该函数需要输入的参数有 灰度图像以及刚才定义的阀值上下限
tight = cv2.Canny(gray, 200, 240)
# Display the images
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(20,10))
ax1.set_title('wide')
ax1.imshow(wide, cmap='gray')
ax2.set_title('tight')
ax2.imshow(tight, cmap='gray')
# Read in the image
image = cv2.imread('images/sunflower.jpg')
# Change color to RGB (from BGR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
# Convert the image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
## TODO: Define lower and upper thresholds for hysteresis
# right now the threshold is so small and low that it will pick up a lot of noise
lower = 100
upper =200
edges = cv2.Canny(gray, lower, upper)
plt.figure(figsize=(20,10))
plt.imshow(edges, cmap='gray')
形状检测
我们知道如何检测图像中对象的边缘,但是我们如何才能开始在对象周围找到统一的边界?
我们希望能够在给定图像中分离和定位多个对象。接下来,我们将讨论霍夫变换,它将图像数据从xy坐标系转换为霍夫空间,在那里可以轻松识别简单的边界,如直线和圆。
一条直线可由两个点A=(X1,Y1)和B=(X2,Y2)确定(笛卡尔坐标)
另一方面,
也可以写成关于(k,q)的函数表达式(霍夫空间):
对应的变换可以通过图形直观表示:
变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。
反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):
再来看看A、B两个点,对应霍夫空间的情形:
一步步来,再看一下三个点共线的情况:
可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。
如果不止一条直线呢?再看看多个点的情况(有两条直线):
其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。
看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。
到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?
k=∞是不方便表示的,而且q怎么取值呢,这样不是办法。因此考虑将笛卡尔坐标系换为:极坐标表示。
ρ表示原点到直线的距离
θ 表示的是直线与横轴所成的角
在极坐标系下,其实是一样的:极坐标的点→霍夫空间的直线,只不过霍夫空间不再是[k,q]的参数,而是[ρ,θ ]的参数,给出对比图:
霍夫线检测
# Import resources and display the image
import numpy as np
import matplotlib.pyplot as plt
import cv2
%matplotlib inline
# Read in the image
image = cv2.imread('images/phone.jpg')
# Change color to RGB (from BGR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
# Perform edge detection
# Convert image to grayscale
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)
plt.imshow(edges, cmap='gray')
#Find lines using a Hough transform
# Define the Hough transform parameters
# Make a blank the same size as our image to draw on
rho = 1
theta = np.pi/180 #其中 ρ 和 θ 就是我们的霍夫变量了,两者定义了检测的分辨率,我把它们分别设为 1 个像素和 1 度
threshold = 60 #然后是直线检测的最低阀值,这个阀值就是霍夫空间要找到一根直线所需的最少相交数
min_line_length = 100 #最小线条长度
max_line_gap = 5 # 线条分段之间的距离
line_image = np.copy(image) #creating an image copy to draw lines on
# Run Hough on the edge-detected image
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
min_line_length, max_line_gap)
#这个函数会返回其检测到的所有霍夫直线,每根直线实际上是一个包含着四个点的数组,四个点即 x1、y1 和 x2、y2,也就是每根直线两端的终点。
# 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)
霍夫圆检测
import numpy as np
import matplotlib.pyplot as plt
import cv2
%matplotlib inline
# Read in the image
image = cv2.imread('images/round_farms.jpg')
# Change color to RGB (from BGR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
# Gray and blur
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
gray_blur = cv2.GaussianBlur(gray, (3, 3), 0)
plt.imshow(gray_blur, cmap='gray')
HoughCircles函数
an input image, detection method (Hough gradient), resolution factor between the detection and image (1),
minDist - the minimum distance between circles
param1 - the higher value for performing Canny edge detection
param2 - threshold for circle detection, a smaller value --> more circles will be detected
min/max radius for detected circles
# for drawing circles on
circles_im = np.copy(image)
## TODO: use HoughCircles to detect circles
# right now there are too many, large circles being detected
# try changing the value of maxRadius, minRadius, and minDist
circles = cv2.HoughCircles(gray_blur, cv2.HOUGH_GRADIENT, 1,
minDist=45,
param1=70,
param2=11,
minRadius=20,
maxRadius=40)
# convert circles into expected type
circles = np.uint16(np.around(circles))
# draw each one
for i in circles[0,:]:
# draw the outer circle
cv2.circle(circles_im,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(circles_im,(i[0],i[1]),2,(0,0,255),3)
plt.imshow(circles_im)
print('Circles shape: ', circles.shape)
Haar Cascades (Haar 级联)
Face detection using OpenCV
One older (from around 2001), but still popular scheme for face detection is a Haar cascade classifier; these classifiers in the OpenCV library and use feature-based classification cascades that learn to isolate and detect faces in an image. You can read the original paper proposing this approach here.
# import required libraries for this section
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import cv2
# load in color image for face detection
image = cv2.imread('images/multi_faces.jpg')
# convert to RBG
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.figure(figsize=(20,10))
plt.imshow(image)
# convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
plt.figure(figsize=(20,10))
plt.imshow(gray, cmap='gray')
Next we load in the fully trained architecture of the face detector, found in the file detector_architectures/ haarcascade_frontalface_default.xml,and use it on our image to find faces!
# 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)
How many faces are detected is determined by the function, detectMultiScale which aims to detect faces of varying sizes. The inputs to this function are: (image, scaleFactor, minNeighbors); you will often detect more faces with a smaller scaleFactor, and lower value for minNeighbors, but raising these values often produces better matches. Modify these values depending on your input image.
# print out the detections found
print ('We found ' + str(len(faces)) + ' faces in this image')
print ("Their coordinates and lengths/widths are as follows")
print ('=============================')
print (faces)
The output of the classifier is an array of detections; coordinates that define the dimensions of a bounding box around each face. Note that this always outputs a bounding box that is square in dimension.
img_with_detections = np.copy(image) # make a copy of the original image to plot rectangle detections ontop of
##### Let's plot the corresponding detection boxes on our original image to see how well we've done.
# 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)
# display the result
plt.figure(figsize=(20,10))
plt.imshow(img_with_detections)