如何使用Python对图片进行角度测量

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算法对其进行边缘提取。然后我们通过寻找图片中的线段,并计算线段与水平方向的角度来得出图片的旋转角度。

后端开发标签