1. 简介
在图像处理中,图像的方向信息对于后续图像处理任务来说是非常重要的。在很多场景中,我们需要知道图片中某个物体或者场景的旋转角度。本文将介绍如何使用Python来实现角度测量的功能。
2. 代码准备
2.1 安装Python库
首先需要安装Python的Pillow库。Pillow是Python中Image处理常用库,其可以实现图像的读取、处理、保存等功能。
!pip install Pillow
2.2 下载图片
本文的案例图片为旋转了45度的方形图,图片如下:
3. 角度测量
3.1 获取图片信息
在进行角度测量之前,我们需要读取图片信息。以下代码展示如何读取图片信息:
from PIL import Image
# 读取图片
img = Image.open("test.jpg")
# 获取图片尺寸信息
width, height = img.size
通过上述代码我们可以获取到图片的尺寸大小,以便后续使用。
3.2 旋转图片
在进行角度测量之前,我们需要对图片进行旋转,使其与x轴对齐。以下代码展示如何将图片旋转指定角度:
import math
# 根据指定的角度旋转图片
def rotate_image(image, angle):
# 将角度转换为弧度
angle = math.radians(angle)
# 计算旋转后的图片尺寸
width, height = image.size
cos_val = math.fabs(math.cos(angle))
sin_val = math.fabs(math.sin(angle))
new_width = int(width * cos_val + height * sin_val)
new_height = int(width * sin_val + height * cos_val)
# 创建空白图片
new_image = Image.new("RGBA", (new_width, new_height), (0, 0, 0, 0))
# 计算图片居中后的位置
x = int(new_width / 2 - width / 2)
y = int(new_height / 2 - height / 2)
# 将旧图片绘制到新图片上
new_image.paste(image, (x, y))
# 旋转图片并将其保存到新的文件中
new_image = new_image.rotate(-angle, expand=True)
return new_image
# 将图片旋转指定角度
img = Image.open("test.jpg")
angle = 45
img = rotate_image(img, angle)
img.show()
在这个过程中,我们需要将指定的角度转换为弧度,然后通过计算出旋转后的图片尺寸,创建一个新图片并将旧图片绘制到新图片上,最后对图片进行旋转。
3.3 边缘提取
在对图片进行角度测量之前,我们需要对图片进行边缘提取,以便更好地分析图片信息。以下代码展示如何对图片进行边缘提取:
from PIL import ImageFilter
# 对图片进行边缘提取
def contour_image(image, threshold=100):
# 转换为灰度图
image = image.convert("L")
# 使用一阶导数边缘检测算法
image = image.filter(ImageFilter.FIND_EDGES)
# 使用Scharr算法对图片进行平滑处理
image = image.filter(ImageFilter.Kernel((3, 3), [-3, 0, 3, -10, 0, 10, -3, 0, 3], scale=1, offset=0))
# 对图片进行二值化处理
image = image.point(lambda x: 255 if x > threshold else 0)
return image
# 对图片进行边缘提取
img = Image.open("test.jpg")
img = contour_image(img)
img.show()
在这个过程中,我们需要将图片转换为灰度图,并使用一阶导数边缘检测算法和Scharr算法对图片进行平滑处理,最后对图片进行二值化处理。
3.4 寻找线段
在对图片进行边缘提取之后,我们需要寻找图片中的线段。以下代码展示如何寻找图片中的线段:
from PIL import ImageDraw
import numpy as np
# 寻找线段
def find_line_segments(image):
# 获取图片像素信息
pixels = np.array(image)
# 将像素值翻转,黑色变为白色,白色变为黑色
pixels = 255 - pixels
# 针对每行像素,统计相邻的黑色像素个数,并将其保存到segments数组中
segments = []
for row in pixels:
segment_start = -1
segment_end = -1
for i, pixel in enumerate(row):
if pixel == 0:
if segment_start == -1:
segment_start = i
segment_end = i
elif segment_start != -1:
segments.append((segment_start, segment_end))
segment_start = -1
segment_end = -1
if segment_start != -1:
segments.append((segment_start, segment_end))
# 过滤出长度为2以上的线段
segments = [s for s in segments if s[1]-s[0] > 1]
return segments
# 寻找线段
img = Image.open("test.jpg")
img = contour_image(img)
segments = find_line_segments(img)
draw = ImageDraw.Draw(img)
for segment in segments:
draw.line((segment[0], 0, segment[1], img.size[1]), fill=128, width=2)
img.show()
在这个过程中,我们需要获取到图片的像素信息,然后对像素信息进行处理,找到相邻的黑色像素,将其保存为线段。最后过滤出长度为2以上的线段。在这个过程中我们还使用了Pillow库中的ImageDraw模块来绘制线段。
3.5 转换角度
在寻找到图片的线段之后,我们可以通过计算线段与水平方向的角度来得到图片的旋转角度。以下代码展示如何计算线段与水平方向的角度:
# 计算线段与水平方向的角度并返回
def calculate_angle(segments):
# 计算每条线段的斜率
slopes = []
for segment in segments:
x_diff = segment[1] - segment[0]
y_diff = img.height
slope = y_diff / x_diff
slopes.append(slope)
# 计算斜率的均值
avg_slope = np.mean(slopes)
# 将斜率转换为角度
angle = np.arctan(avg_slope) * 180 / np.pi
return angle
# 计算角度
angle = calculate_angle(segments)
print("旋转角度为:", angle)
在这个过程中,我们需要针对所有线段计算出其斜率,并求出斜率的均值。然后将斜率转换为角度,并返回旋转角度。
4. 完整代码
以下是完整的代码:
from PIL import Image, ImageFilter, ImageDraw
import math
import numpy as np
# 根据指定的角度旋转图片
def rotate_image(image, angle):
# 将角度转换为弧度
angle = math.radians(angle)
# 计算旋转后的图片尺寸
width, height = image.size
cos_val = math.fabs(math.cos(angle))
sin_val = math.fabs(math.sin(angle))
new_width = int(width * cos_val + height * sin_val)
new_height = int(width * sin_val + height * cos_val)
# 创建空白图片
new_image = Image.new("RGBA", (new_width, new_height), (0, 0, 0, 0))
# 计算图片居中后的位置
x = int(new_width / 2 - width / 2)
y = int(new_height / 2 - height / 2)
# 将旧图片绘制到新图片上
new_image.paste(image, (x, y))
# 旋转图片并将其保存到新的文件中
new_image = new_image.rotate(-angle, expand=True)
return new_image
# 对图片进行边缘提取
def contour_image(image, threshold=100):
# 转换为灰度图
image = image.convert("L")
# 使用一阶导数边缘检测算法
image = image.filter(ImageFilter.FIND_EDGES)
# 使用Scharr算法对图片进行平滑处理
image = image.filter(ImageFilter.Kernel((3, 3), [-3, 0, 3, -10, 0, 10, -3, 0, 3], scale=1, offset=0))
# 对图片进行二值化处理
image = image.point(lambda x: 255 if x > threshold else 0)
return image
# 寻找线段
def find_line_segments(image):
# 获取图片像素信息
pixels = np.array(image)
# 将像素值翻转,黑色变为白色,白色变为黑色
pixels = 255 - pixels
# 针对每行像素,统计相邻的黑色像素个数,并将其保存到segments数组中
segments = []
for row in pixels:
segment_start = -1
segment_end = -1
for i, pixel in enumerate(row):
if pixel == 0:
if segment_start == -1:
segment_start = i
segment_end = i
elif segment_start != -1:
segments.append((segment_start, segment_end))
segment_start = -1
segment_end = -1
if segment_start != -1:
segments.append((segment_start, segment_end))
# 过滤出长度为2以上的线段
segments = [s for s in segments if s[1]-s[0] > 1]
return segments
# 计算线段与水平方向的角度并返回
def calculate_angle(segments):
# 计算每条线段的斜率
slopes = []
for segment in segments:
x_diff = segment[1] - segment[0]
y_diff = img.height
slope = y_diff / x_diff
slopes.append(slope)
# 计算斜率的均值
avg_slope = np.mean(slopes)
# 将斜率转换为角度
angle = np.arctan(avg_slope) * 180 / np.pi
return angle
# 读取图片
img = Image.open("test.jpg")
# 将图片旋转指定角度
angle = 45
img = rotate_image(img, angle)
# 对图片进行边缘提取
img = contour_image(img)
# 寻找线段
segments = find_line_segments(img)
# 计算角度
angle = calculate_angle(segments)
# 绘制线段
draw = ImageDraw.Draw(img)
for segment in segments:
draw.line((segment[0], 0, segment[1], img.size[1]), fill=128, width=2)
# 显示结果
img.show()
print("旋转角度为:", angle)
运行上述代码将得到下面的输出结果和图片:
旋转角度为: -43.48527421629353
5. 总结
本文介绍了如何使用Python对图片进行角度测量。在实现过程中,我们使用了Pillow库中的Image模块、ImageFilter模块以及ImageDraw模块等。我们首先对图片进行了旋转操作,并使用一阶导数边缘检测算法和Scharr算法对其进行边缘提取。然后我们通过寻找图片中的线段,并计算线段与水平方向的角度来得出图片的旋转角度。