实验一:图像处理基本操作
一、实验目的
1、熟悉并掌握MATLAB工具的使用;
2、实现图像的读取、显示、存储、平移、镜像、放大、缩小及旋转操作;
3、掌握常用的插值方法,并了解其优缺点。
二、实验环境
IDE:pycharm
Python
三、实验内容(题目、相关知识(函数,原理…)、代码、实验结果)
1、读入一幅RGB图像(自选),变换为灰度图像和二值图像,并在同一个窗口内分别显示RGB图像和灰度图像,注上文字标题,并将结果以文件形式存到磁盘上。
灰度变换属于空域变换增强技术。
目的:
一般成像系统形成图像的亮度有限,对比度不足,使图像的视觉效果差,灰度变换即可有效地改善视觉效果。
概念:
灰度变换是一种点操作,根据原始图像中每个像素的灰度值,按照某种映射规则将其转化为另一灰度值。灰度变换可有效改善图像的视觉效果,变换原理可表示为如式所示:
t = E(k)
式中:k为原始图像的灰度值f(x,y),t为变换图像的灰度值g(x,y),E()为灰度增强函数。
原理:
灰度变换原理可由图来说明,其中,图(a)为原始图像,具有两种灰度级,分别用B和W来表示;图(b)为灰度增强函数,根据函数的映射规则,原始图像的灰度值B映射为灰度值W,原始图像的灰度值W映射为灰度值B;图(c)为变换后的图像。
灰度变换的关键在于根据增强要求设计灰度映射规则,即设计灰度增强函数。典型灰度变换函数包括:比例线性变换、分段线性变换、非线性变换
图像二值化就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。二值图像每个像素只有两种取值:要么纯黑,要么纯白。由于二值图像数据足够简单,许多视觉算法都依赖二值图像。通过二值图像,能更好地分析物体的形状和轮廓。二值图像也常常用作原始图像的掩模(又称遮罩、蒙版,Mask):它就像一张部分镂空的纸,把我们不感兴趣的区域遮掉。进行二值化有多种方式,其中最常用的就是采用阈值法(Thresholding)进行二值化。
转换为灰度图像使用函数cvColor,这里转化为二值图像直接设置阈值判断灰度值大小,如果灰度值大于阈值直接赋值为255,反之赋值为0
实验代码:
实验结果:
2、对图像(自选)执行平移、镜像(水平镜像、垂直镜像)放大、缩小及旋转操作,其中放大、旋转操作分别采用最近邻插值及双线性插值方法实现,要求根据算法自己编写代码实现,并分析两种插值方法的优缺点。
(1)平移
图像平移首先定义平移矩阵M,再调用warpAffine()函数实现平移,python函数如下:
M = np.float32([[1, 0, x], [0, 1, y]])
M表示平移矩阵,其中x表示水平平移量,y表示垂直平移量
shifted = cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])
src表示原始图像
M表示平移矩阵
dsize表示变换后的输出图像的尺寸大小
dst为输出图像,其大小为dsize,类型与src相同
flag表示插值方法的组合和可选值
borderValue表示像素外推法,当borderMode = BORDER_TRANSPARENT时,表示目标图像中的像素不会修改源图像中的“异常值”。
borderValue用于边界不变的情况,默认情况下为0
平移后图像
(2)旋转
旋转前:
x0=rcosb,y0=rsinb
旋转后:
x1=rcos(b-a)=rcosbcosa+rsinbsina=x0cosa+y0sina
y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa
一般3-D旋转的变换。将一个空间点P绕一个中心点C旋转可用连续的3个变换来实现:
第1个变换:平移点C到坐标原点。
第2个变换:将P绕原点旋转。
第3个变换:平移点C回到原始位置。
本实验中要求旋转采用双线性插值法
如下为主要算法代码:
import math
import random
import cv2
import numpy as np
def bilinear_rotate(imgArray):
H, W, channel = imgArray.shape
pi = math.pi
theta = random.randint(0,360)
angle = theta * pi / 180
matrix1 = np.array([[1, 0, 0],
[0, -1, 0],
[-0.5 * H, 0.5 * W, 1]])
matrix2 = np.array([[math.cos(angle), -math.sin(angle), 0],
[math.sin(angle), math.cos(angle), 0],
[0, 0, 1]])
matrix3 = np.array([[1, 0, 0],
[0, -1, 0],
[0.5 * H, 0.5 * W, 1]])
new_data = np.zeros_like(imgArray,dtype=np.uint8)
for i in range(H):
for j in range(W):
dot1 = np.matmul(np.array([i, j, 1]), matrix1)
dot2 = np.matmul(dot1, matrix2)
dot3 = np.matmul(dot2, matrix3)
new_coordinate = dot3
new_i = int(math.floor(new_coordinate[0]))
new_j = int(math.floor(new_coordinate[1]))
u = new_coordinate[0] - new_i
v = new_coordinate[1] - new_j
if new_j>=W or new_i >=H or new_i<1 or new_j<1 or (i+1)>=H or (j+1)>=W:
continue
if (new_i + 1)>=H or (new_j+1)>=W:
new_data[i, j, :] = imgArray[new_i,new_j, :]
else:
new_data[i, j, :] = (1-u)*(1-v)*imgArray[new_i,new_j, :] + \
(1-u)*v*imgArray[new_i,new_j+1, :] + \
u*(1-v)*imgArray[new_i+1,new_j, :] +\
u*v*imgArray[new_i+1,new_j+1, :]
return new_data
image = cv2.imread('Asen.jpg')
new_image = bilinear_rotate(image)
cv2.imshow('Rotated image', new_image)
运行结果:
(3)缩放
缩放采用最邻近插值缩放
插值的目的是根据已知的图像的像素值获得未知目标图像的像素值
其中src表示原始图像,tar表示插值得到的目标图像,H和W分别表示图像的高度和宽度。插值的核心是找到(tar_x, tar_y)和(src_x, src_y)的映射关系,从而实现对目标图像的每一个像素点进行赋值。假设目的是将原始图像长度和宽度扩大(3,4)倍,即:
ratio_H = tar_H/src_H = tar_x/src_x = 3
ratio_W = tar_W/src_W = tar_y/src_y = 4
通过上式变形,得到目标图像的像素点和原始图像的像素点映射如下:
tar_x = src_x/ratio_H
tar_y = src_y/ratio_W
知道了像素点之间的映射关系,实现算法就很容易了,算法流程如下:
根据tar_H和tar_W创建目标图像
计算缩放比例因子ratio
遍历目标图像每个像素点,计算映射关系
遍历目标图像每个像素点,使用对应原始图像的对应像素点对其赋值
算法代码:
import cv2
import numpy as np
def test(img, Weight_out, Height_out):
# 获取图像的大小
Height_in, Weight_in, _ = img.shape
# 创建输出图像
outimg = np.zeros((Height_out, Weight_out, 3), dtype=np.uint8)
for x in range(Height_out - 1):
for y in range(Weight_out - 1):
# 计算输出图像坐标(i,j)坐标使用输入图像中的哪个坐标来填充
x_out = round(x * (Height_in / Height_out))
y_out = round(y * (Weight_in / Weight_out))
# 插值
outimg[x, y] = img[x_out, y_out]
return outimg
# 读取图像
img = cv2.imread('Asen.jpg')
test = test(img, int(img.shape[1] * 1.5), int(img.shape[0] * 1.5))
# OpenCV函数
OpenCV_test = cv2.resize(img, (int(img.shape[1] * 1.5), int(img.shape[0] * 1.5)))
cv2.imshow("nearest neighbor", test) # 近邻插值缩放后
cv2.imshow("image", img) # 原图
cv2.waitKey(0)
运行结果:
(4)镜像
opencv的函数flip()可以实现图像沿x轴翻转、沿y轴翻转、同时沿x轴和y轴翻转,从而实现图像的水平镜像和垂直镜像。
函数flip()的C++原型如下:
void cv::flip(InputArray src,
OutputArray dst,
int flipCode )
如果flipCode的值大于0,表示绕y轴翻转,对应于实现图像的水平镜像;
如果flipCode的值等于0,表示绕x轴翻转,对应于实现图像的垂直镜像;
如果flipCode的值小于0,表示同时绕x轴和y轴翻转,对应于同时实现图像的垂直镜像和水平镜像
运行结果:
四、实验心得
本次图像处理实验主要是对图像进行了灰度值的变化,以及平移、镜像、放大、旋转等操作。我使用的是python,python中的opencv库中包含许多丰富图像处理函数,我们可以方便的直接使用这些封装好的函数,但是对于我们学习这门课程的主要意义不仅仅是会进行图像处理,还要明白图像处理的原理,以及函数实现的过程。这就要求我们可以不使用库函数的情况下,自己编写相应的插值方法来实现图像的放大缩小等操作。总而言之,图像处理对我们生活方方面面影响巨大,我们学习这门课程要懂得图像处理原理,并运用于实践当中。