目录
1.基础知识
1.图像几何变换概念
2.图像几何变换方式
3.插值运算
4.几何变换步骤
2.各类变换
1.位置变换
2.形状变换
3.代数运算
3.实战演练
1.基础知识
1.图像几何变换概念
- 在图像处理过程中,为了观测需要,常常需要对 图像进行几何变换,如几何失真图像的校正、图 像配准、电影、电视和媒体广告等的影像特技处 理等,是图像变形以及校正变形的基础。
- 图像几何变换将图像中任一像素映射到一个新位置,是一种空间变换,关键在于确定图像中点与点之间的映射关系
2.图像几何变换方式
- 首先有齐次坐标:用n+1维向量表示n维向量的方法称为齐次坐标 表示法。原图像用点集(转置的意思)表示。好处:齐次坐标中,对原图像进行平移、缩放、旋转等 几何变换,可用一个变换矩阵表示。
- 然后是变换矩阵:a,b,c,d用于图形的比例、对称、 错切、旋转等基本变换;k,m用于 图形的平移变换;p,q用于投影变 换;s用于全比例变换。
- 实现2D图像几何变换的基本变换的一般过程是:变换矩阵T×变换前的点集矩阵=变换后的点集矩阵。
3.插值运算
-
为什么会有这玩意:当你调整图像的大小,特别是缩小图像时,由于目标大小的像素数量少于原图像的像素数量,就需要通过插值算法来估算新像素的值。在进行旋转、平移、拉伸等几何变换时,图像的像素位置可能会发生变化(比如原x,y的像素点变成0.8x,0.9y算出来是个小数,则相应位置像素值不知道是多少)应用一些滤波器或处理器时,如模糊、锐化等操作,会导致像素值的变化,需要通过插值来重新计算像素值。
-
概念:指利用已知邻近像素点的灰度值来产生未知像素点的灰度值(各通道)
-
常用插值运算方法:
- 最近邻插值:非整数像素灰度值就等于距离最近 的坐标都为整数的像素的灰度值。
- 双线性插值:利用非整数像素点周围的四个像素 点的相关性,通过双线性算法计算得出。图1算法或图2算法(利用周边四个点)。
- 双三次插值:利用非整数像素点周围的16个像素 点进行计算。
4.几何变换步骤
- 根据不同的几何变换公式计算新图像的尺寸
- 根据几何变换的逆变换,对新图像中的每一点确 定其在原图像中的对应点
- 按对应关系给新图像中各个像素赋值
- 若新图像中像素点在原图像中的对应点坐标存 在,直接赋值
- 若新图像中像素点在原图像中的对应点坐标超 出图像宽高范围,直接赋背景色
- 若新图像中像素点在原图像中的对应点坐标在 图像宽高范围内,采用插值的方法计算
2.各类变换
1.位置变换
- 图像的位置变换是指图像的大小和形状不发生变化,只是图像像素点的位置发生变化,含平 移、镜像、旋转
- 平移:若不想丢失信息,可能需要扩大画布
- 镜像:M为总行数,N为总列数
- 旋转: θ为正,代表逆时针旋转,若要确定变换后的尺寸大小:计算原图像四个角在 旋转后的坐标;确定新图像的分辨率,M'=max x'-min x'+1;N'=max y'-min y'+1(结果向上取整).对于不在原点旋转的,先要将坐标系平移到原点,再按绕原点旋转进行变换,然后平移回原坐标原点。得到新图像的M',N'后,需依次确定[0,M'-1]+minx',[0,N'-1]+miny'各个像素点的值。也就是再逆变换回去,根据步骤第三点的三条进行赋值。
2.形状变换
- 缩放
- 错切:平面景物在投影平面上的非垂直投 影,使图像中的图形产生扭变
3.代数运算
- 加法运算如下图,应用主要有:多幅图像相加求平均去除叠加性噪声,将一幅图像的内容经配准后叠加到另一幅图像 上去,以改善图像的视觉效果,在多光谱图像中,通过加法运算加宽波段,如 绿色波段和红色波段图像相加可以得到近似全色图像,用于图像合成和图像拼接。
- 减法运算如下图,应用主要有:显示两幅图像的差异,检测同一场景两幅图像 之间的变化;去除不需要的叠加性图案,加性图案可能是缓 慢变化的背景阴影或周期性的噪声,或在图像 上每一个像素处均已知的附加污染等;图像分割:如分割运动的车辆,减法去掉静止 部分,剩余的是运动元素和噪声;生成合成图像。
- 乘法运算主要是部分图框出来,也就是图像的局部显示和提取:用二值模板图像与 原图像做乘法来实现 。
- 逻辑运算:原理如图。
3.实战演练
P1. 试编写程序,对图像逆时针旋转60°,采用双线性插值的方法
from PIL import Image # 导入PIL库,用于图像处理
import math #用于旋转时计算三角函数
def bilinear_interpolation(image, x, y):
# 将坐标转换为整数
x1, y1 = int(x), int(y)
x2, y2 = x1 + 1, y1 + 1
# 处理边界情况
if x2 >= image.width:
x2 = x1
if y2 >= image.height:
y2 = y1
# 获取四个相邻像素的RGB值
q11 = image.getpixel((x1, y1))
q21 = image.getpixel((x2, y1))
q12 = image.getpixel((x1, y2))
q22 = image.getpixel((x2, y2))
r = []#空列表的创建
# 处理 RGB 值的每个通道
for i in range(3): # 0: 红色通道, 1: 绿色通道, 2: 蓝色通道
r.append(q11[i] * (x2 - x) * (y2 - y) + q21[i] * (x - x1) * (y2 - y) + q12[i] * (x2 - x) * (y - y1) + q22[i] * (x - x1) * (y - y1))
return tuple(map(int, r)) # 返回处理后的 RGB 值
def rotate_image(image, angle):
# 旋转图像函数
angle = angle % 360
if angle == 0:
return image
rotated_image = Image.new("RGB", image.size) # 创建旋转后的图像对象
for x in range(rotated_image.width):
for y in range(rotated_image.height):
# 计算旋转后的坐标,即逆变换找到原坐标位置
x_original = ((x - rotated_image.width / 2) * math.cos(math.radians(angle)) -
(y - rotated_image.height / 2) * math.sin(math.radians(angle)) + image.width / 2)
y_original = ((x - rotated_image.width / 2) * math.sin(math.radians(angle)) +
(y - rotated_image.height / 2) * math.cos(math.radians(angle)) + image.height / 2)
if 0 <= x_original < image.width - 1 and 0 <= y_original < image.height - 1:
# 应用双线性插值
rotated_image.putpixel((x, y), bilinear_interpolation(image, x_original, y_original))#注意写法,这个函数第二个参数是元组表示的一组rgb的值
return rotated_image
# 打开图像文件
image = Image.open("rainbow.jpg")
# 逆时针旋转60°
rotated_image = rotate_image(image, 60)
# 保存旋转后的图像
rotated_image.save("rotated_image.jpg") # 保存旋转后的图像
说明:map(int, r)
: 这部分使用了Python内置的 map
函数,它接受一个函数和一个可迭代对象作为参数。在这里,int
函数被应用于可迭代对象 r
中的每个元素,将每个元素转换为整数。tuple(...)
: tuple()
函数用于将一个可迭代对象转换为元组(tuple)。在这里,map(int, r)
返回一个迭代器,然后 tuple()
将这个迭代器转换为一个由整数组成的元组。
P2. 打开一幅图像,依次完成下列要求:顺时针旋转20°,做水平镜像,做错切变换,缩小图像。若需要插值运算,采用双线性插值方法;要求输出显示原图、中间结果和最后结果。(作为模板题)
import math #用于旋转时计算三角函数
from PIL import Image
def resize_image(image, width_scale, height_scale):
# 获取图像的宽度和高度
width, height = image.size
# 计算放大或缩小后的宽度和高度
new_width = int(width * width_scale)
new_height = int(height * height_scale)
# 创建一个新图像对象,用于存储放大或缩小后的图像
resized_image = image.resize((new_width, new_height))
return resized_image
def shear_image(image, shear_factor):
# 获取图像的宽度和高度
width, height = image.size
# 计算错切后的宽度,只需计算宽度即可
new_width = width + int(abs(shear_factor) * height)
# 创建一个新图像对象,用于存储错切后的图像
sheared_image = Image.new("RGB", (new_width, height))
# 错切处理
for x in range(new_width):
for y in range(height):
# 计算错切后的坐标,逆旋转
new_x = x - int(shear_factor * y)
if 0 <= new_x < width:
# 获取原始图像中的像素值
pixel = image.getpixel((new_x, y))
# 将像素值复制到错切图像中
sheared_image.putpixel((x, y), pixel)
return sheared_image
def mirror_image(image):
# 获取图像的宽度和高度
width, height = image.size
# 创建一个新图像对象,用于存储镜像后的图像
mirrored_image = Image.new("RGB", (width, height))
# 镜像处理
for x in range(width):
for y in range(height):
# 获取原始图像中的像素值
pixel = image.getpixel((x, y))
# 将像素值复制到镜像图像中,但是在水平方向上镜像
mirrored_image.putpixel((width - x - 1, y), pixel)
return mirrored_image
def bilinear_interpolation(image, x, y):
# 将坐标转换为整数
x1, y1 = int(x), int(y)
x2, y2 = x1 + 1, y1 + 1
# 处理边界情况
if x2 >= image.width:
x2 = x1
if y2 >= image.height:
y2 = y1
# 获取四个相邻像素的RGB值
q11 = image.getpixel((x1, y1))
q21 = image.getpixel((x2, y1))
q12 = image.getpixel((x1, y2))
q22 = image.getpixel((x2, y2))
r = []#空列表的创建
# 处理 RGB 值的每个通道
for i in range(3): # 0: 红色通道, 1: 绿色通道, 2: 蓝色通道
r.append(q11[i] * (x2 - x) * (y2 - y) + q21[i] * (x - x1) * (y2 - y) + q12[i] * (x2 - x) * (y - y1) + q22[i] * (x - x1) * (y - y1))
return tuple(map(int, r)) # 返回处理后的 RGB 值
def rotate_image(image, angle):
# 旋转图像函数
angle = angle % 360
if angle == 0:
return image
rotated_image = Image.new("RGB", image.size) # 创建旋转后的图像对象
for x in range(rotated_image.width):
for y in range(rotated_image.height):
# 计算旋转后的坐标,即逆变换找到原坐标位置
x_original = ((x - rotated_image.width / 2) * math.cos(math.radians(angle)) -
(y - rotated_image.height / 2) * math.sin(math.radians(angle)) + image.width / 2)
y_original = ((x - rotated_image.width / 2) * math.sin(math.radians(angle)) +
(y - rotated_image.height / 2) * math.cos(math.radians(angle)) + image.height / 2)
if 0 <= x_original < image.width - 1 and 0 <= y_original < image.height - 1:
# 应用双线性插值
rotated_image.putpixel((x, y), bilinear_interpolation(image, x_original, y_original))#注意写法,这个函数第二个参数是元组表示的一组rgb的值
return rotated_image
# 打开图像文件
image = Image.open("rainbow.jpg")
image.show()
# 逆时针旋转-20°
rotated_image = rotate_image(image, -20)
rotated_image.show()
mirror=mirror_image(rotated_image)
mirror.show()
share=shear_image(mirror,2)
share.show()
resize=resize_image(share,0.2,0.2)
resize.show()
P3. 打开两幅图像,利用几何变换、图像代数运算,生成一幅精美的合成图像
from PIL import Image
def resize_image(image, width_scale, height_scale):
# 获取图像的宽度和高度
width, height = image.size
# 计算放大或缩小后的宽度和高度
new_width = int(width * width_scale)
new_height = int(height * height_scale)
# 创建一个新图像对象,用于存储放大或缩小后的图像
resized_image = image.resize((new_width, new_height))
return resized_image
def add_images(sun_image, sky_image, output_path):
# 获取太阳图像和天空图像的宽度和高度
sun_width, sun_height = sun_image.size
sky_width, sky_height = sky_image.size
# 确保太阳图像完全在天空图像内
if sun_width <= sky_width and sun_height <= sky_height:
# 创建一个新的图像对象,用于存储混合后的图像
result_image = Image.new("RGB", (sky_width, sky_height))
# 将天空图像复制到结果图像中
result_image.paste(sky_image, (0, 0))
# 将太阳图像叠加到左上角
result_image.paste(sun_image, (0, 0), sun_image)
# 保存混合后的图像
result_image.show()
else:
print("Sun image is too large to fit entirely on the sky image.")
# 图像文件路径
sun_path = "sun.png"
sun_image = Image.open(sun_path)
sun_image.show()
sun=resize_image(sun_image,0.2,0.2)
sky_path = "sky.jpg"
output_path = "result_image.jpg"
sky_image = Image.open(sky_path)
sky_image.show()
# 执行图像混合操作
add_images(sun, sky_image, output_path)
说明,paste函数的使用:语法为image.paste(image_to_paste, box, mask),image_to_paste
: 要粘贴的图像对象。box
: 表示将图像粘贴到另一个图像的位置,通常是一个元组 (x, y)
,表示左上角的坐标。mask
: 用于指定一个掩码图像,一般而言为数值,从0不透明到255透明,也可以是一个二值图像,如果提供了掩码图像,它将根据掩码的像素值来控制被粘贴图像的透明度,使得被粘贴图像可以部分透明地展现在目标图像上。
题外话:不要吐槽图像的精美程度。