一文教你使用HSV颜色模型和openCV构建昼夜分类器
磐创AI在本文中,我们将学习如何构建一个简单的模型,它使用色调饱和度值 (HSV) 颜色模型作为特征提取,opencv 进行图像处理的基础来对白天和黑夜进行分类。
介绍
色调饱和度值(HSV)是RGB的替代颜色模型。色调(H)是颜色轮中的三种主色和三种次色。饱和度是颜色的纯度和强度,其越低,颜色越接近灰色。值 (V) 是指颜色的相对亮度或暗度。这些值中的每一个都有一个限制;H 从 0 到 360,S 和 V 从 0 到 100。
我们将利用颜色模型的 Value (V) 属性。决定图像亮度的图像值 (V)。这是我们要提取的特征。然后我们将设置一个阈值,可以将白天图像与夜间图像分开。
虽然我们可以设置一个特定的阈值,但我们尝试使用训练图像,通过基本推导找到一个最佳阈值。
我们将使用 opencv 库从图像中提取这些特征。
先决条件
- Python
- pip
- opencv
- numpy
- matplotlib
Opencv 是一个计算机视觉包,我们将使用它来处理图像和操作它。numpy用于数值计算,matplotlib用于图像显示。
1. 导入用于测试的库和图像
我们将使用的是用于训练和测试的室外图像
该图像已被标注,即分类为白天和夜间图像。
from util import daynight_helper
import matplotlib.pyplot as plt
import cv2
import numpy as np
#path to the folder where the images are
training_data_path = 'data/day_night_images/training'
2. 预处理图像及其标签我们已经指定了训练图像的路径,但原始图像有噪声并且没有很好地优化分析。
例如,图像可能具有不同的大小,或者标签可能是我们的文字。这是设计模型的第一步。数据清洗。所以我们会写一个助手来帮助我们。
这是我们的图像文件夹结构的样子
data /
day_night_images /
training /
day /
img001.jpg
img002.jpg
night /
img100.jpg
img101.jpg
test /
day /
img001.jpg
img002.jpg
night /
img100.jpg
img101.jpg
因此,我们希望我们的助手读取这个目录并输出带有相应标签的图像,以便img001.jpg有一个日期标签。
我们还将使用数字对标签进行编码。1代表白天,0代表夜晚。
这是代码:
import os
import glob
import matplotlib.image as mpimg
import cv2
def load_dataset(image_dir):
img_list = []
img_types = ['day', 'night']
for img_type in img_types:
for file in glob.glob(os.path.join(image_dir, img_type, '*')):
img = mpimg.imread(file)
if not img is None:
img_list.append((img, img_type))
return img_list
def standardize_image(image):
std_img= cv2.resize(image, (1110, 600))
return std_img
def encode_label(label):
if label== 'day':
return 1
else:
return 0
def standardize_inputs(img_list):
std_list = []
for img in img_list:
std_img = standardize_image(img[0])
std_label = encode_label(img[1])
std_list.append((std_img, std_label))
return std_list
第 8-18 行负责检查文件夹中的所有图像,如果是图像,则将其添加到列表
img_list
中第 24 行是我们标准化图像的地方,在这种特殊情况下,我们将其大小调整为 1110 x 600
第 28 -32 行是我们将标签从文本编码为数字的地方。如果是白天,则为1,否则 0
第 34-43 行是我们从第 8 行创建的列表,对图像进行标准化并对标签进行编码。
让我们在我们的项目中导入它并运行以下代码以查看是否设置了助手程序。
training_data_path = 'data/day_night_images/training'
training_data = daynight_helper.load_dataset(training_data_path)
std_training_data = daynight_helper.standardize_inputs(training_data)
img = std_training_data[0][0]
label = std_training_data[0][1]
plt.imshow(img)
print('Shape ', img.shape)
print('label: ', label)
这应该为你提供Shape (600, 1110, 3) 标签的输出:1
3.获取图片的平均亮度
接下来,我们将编写一个函数(基于 HSV 颜色模型)来获取图像的平均亮度。因此,这个分类问题的特征是亮度。我们正在提取并使用它来解释新图像。
#feature extraction - brightness using hsv
def brightness_value(img):
hsv_img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
v_values = np.sum(hsv_img[:, :, 2])
area = img.shape[0] * img.shape[1]
avg_brightness = v_values/area
return avg_brightness
基本上,我们选择 HSV 颜色模型的 V 值并将其除以图像面积。
在示例图像上运行此函数以了解图片的亮度。
4. 使用选定的标记昼夜差异的阈值来估计标签
一旦我们获得了图片的平均亮度,我们就可以标记一个阈值来划分白天和夜晚的图片。
这个标志着昼夜的差异是我们最大的盟友。请记住,阈值是平均亮度的函数。
这是预测标签的代码:
def estimate_label(img, threshold):
avg_brightness = brightness_value(img)
predicted_label = 0
threshold = threshold
if avg_brightness > threshold:
predicted_label = 1
return predicted_label, avg_brightness
很简单吧?
但是我们怎么知道阈值呢?
答案:我们在计算了一些白天和黑夜图像的平均亮度后进行选择,然后我们从中做出直观的猜测。
这是我们的出发点。对于这种情况,我们选择 120。
但是 120 是最佳阈值吗?
5. 寻找最优阈值
我们可以在阈值为 120 时结束我们的模型预测,我们的准确率为 86%。但是,我们是否可以通过调整或修改阈值来提高准确度,以接近在白天和黑夜之间划出细线的那个点——比如白天和黑夜本身。
因此看看优化器代码:
def estimate_label(img, threshold):
avg_brightness = brightness_value(img)
predicted_label = 0
threshold = threshold
if avg_brightness > threshold:
predicted_label = 1
return predicted_label, avg_brightness
在这里,我们根据阈值估计标签,如果它是正确的,我们就接着下一步。
如果不是,我们计算当前阈值和平均亮度的平均值——因为阈值是基于平均亮度的。
threshold = 120
for i in range(0, len(std_training_data)):
img_data = std_training_data[i]
threshold = optimize_threshold(img_data, threshold)
print('threshold ', threshold)
#threshold 116
运行这个会产生一个新的阈值——116。
PS:可以以不同的方式调整阈值,即平均阈值和平均亮度。这是我在这段代码中使用的技术。
6. 在测试图像上运行分类器
我们有我们的阈值和估算器。让我们在测试图像上运行我们的模型,看看表现如何。
import random
test_data_path = 'data/day_night_images/test'
test_data = daynight_helper.load_dataset(test_data_path)
test_data = daynight_helper.standardize_inputs(test_data)
random.shuffle(test_data)
threshold = 116
correctly_classified = []
misclassified = []
for i in range(0, len(test_data)):
img_data = test_data[i]
pred, avg_brightness = estimate_label(img_data[0], threshold)
#print('predicted ', pred)
label = img_data[1]
#print('label ', label)
if pred == label:
correctly_classified.append(img_data)
else:
misclassified.append(img_data)
#total : 160
#Correct predictions: 140
#Misclassified: 20
#Accuracy 0.875
我们从清理我们的测试数据开始,然后对其进行打乱(第 7 行)。因为我们已经有了阈值。我们可以估计标签。然后记录输出——正确分类与否。
与仅选择 120 作为阈值时的 86% 相比,我们对此的准确率为 87.5%。而且还有改进的空间。
思考。还可以做些什么来进一步提高准确性?你能否添加另一个功能来跟踪平均亮度?有没有办法将阈值移动到最佳值?