【问题标题】:How can I classify different images with various sizes and formats in scikit-learn?如何在 scikit-learn 中对不同大小和格式的不同图像进行分类?
【发布时间】:2019-11-05 05:17:43
【问题描述】:

我正在尝试使用 scikit-learn 构建一个简单的图像分类器。我希望避免在训练前调整和转换每张图像的大小。

问题

鉴于两个不同格式和大小的不同图像(1.jpg2.png),我如何在拟合模型时避免 ValueError

  • 我有一个例子,我只使用1.jpg 进行训练,它非常适合。
  • 我有另一个例子,我同时使用1.jpg2.png 进行训练,并生成了ValueError

这个例子很适合:

import numpy as np
from sklearn import svm 
import matplotlib.image as mpimg

target = [1, 2]
images = np.array([
    # target 1
    [mpimg.imread('./1.jpg'), mpimg.imread('./1.jpg')],
    # target 2
    [mpimg.imread('./1.jpg'), mpimg.imread('./1.jpg')],
])
n_samples = len(images)
data = images.reshape((n_samples, -1))
model = svm.SVC()
model.fit(data, target)

此示例将引发值错误。

观察目标2中不同的2.png图像。

import numpy as np
from sklearn import svm 
import matplotlib.image as mpimg

target = [1, 2]
images = np.array([
    # target 1
    [mpimg.imread('./1.jpg'), mpimg.imread('./1.jpg')],
    # target 2
    [mpimg.imread('./2.png'), mpimg.imread('./1.jpg')],
])
n_samples = len(images)
data = images.reshape((n_samples, -1))
model = svm.SVC()
model.fit(data, target)
# ValueError: setting an array element with a sequence.

1.jpg

2.png

【问题讨论】:

  • 是的,错误是合理的。您不能在不同维度上进行训练,然后在其他维度上进行测试/预测。你试过缩小尺寸吗?您可以将所有图像缩小到相同大小,然后进行任何机器学习
  • 如果我在这里发布我的 stackexchange 问题的链接会不合适吗?
  • 如果能简化解决这个问题就好了!谢谢。
  • 好的,所以我不发帖,因为它属于文本分类,它更像是询问建议之类的问题。 :)

标签: image numpy matplotlib scikit-learn classification


【解决方案1】:

为此,我真的建议使用Keras 中的工具,这些工具专门设计用于以高度可扩展和高效的方式预处理图像。

from keras.preprocessing.image import ImageDataGenerator
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np

1 确定新图片的目标尺寸

h,w = 150,150 # desired height and width
batch_size = 32 
N_images = 100 #total number of images

Keras 是批量工作的,所以batch_size 只是决定一次处理多少张图片(这不会影响您的最终结果,只会影响速度)。

2 创建您的图像生成器

train_datagen = ImageDataGenerator(
    rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    'Pictures_dir',
    target_size=(h, w),
    batch_size=batch_size,
    class_mode = 'binary')

要进行图像提取的对象是ImageDataGenerator。它有 flow_from_directory 的方法,我相信它在这里可能对你有用。它将读取文件夹Pictures_dir 的内容,并期望您的图像按类别位于文件夹中(例如:Pictures_dir/class0 和 Pictures_dir/class1)。生成器在调用时将从这些文件夹创建图像并导入它们的标签(在本例中为“class0”和“class1”)。

这个生成器还有很多其他参数,您可以在Keras 文档中查看它们(特别是如果您想进行数据扩充)。

注意:这将根据您的要求拍摄任何图像,无论是 PNG 还是 JPG

如果您想获取从类名到标签索引的映射,请执行以下操作:

train_generator.class_indices
# {'class0': 0, 'class1': 1}

你可以检查发生了什么

plt.imshow(train_generator[0][0][0])

3 从生成器中提取所有调整大小的图像

现在您可以从ImageGenerator 中提取图像了:

def extract_images(generator, sample_count):
    images = np.zeros(shape=(sample_count, h, w, 3))
    labels = np.zeros(shape=(sample_count))
    i = 0
    for images_batch, labels_batch in generator: # we are looping over batches
        images[i*batch_size : (i+1)*batch_size] = images_batch
        labels[i*batch_size : (i+1)*batch_size] = labels_batch
        i += 1
        if i*batch_size >= sample_count:
            # we must break after every image has been seen once, because generators yield indifinitely in a loop
            break
    return images, labels

images, labels = extract_images(train_generator, N_images)

print(labels[0])
plt.imshow(images[0])

现在,您在images 中拥有所有相同大小的图像,在labels 中拥有它们相应的标签,然后您可以将其输入到您选择的任何scikit-learn 分类器中。

【讨论】:

  • 加载图片和调整图片大小的好方法!试图弄清楚如何将它们放入 train_test_split 和 model.fit 中。 sci-kit 似乎只处理二维数组,而 images.shape 是 (N, 150, 150, 3)。感谢您的帮助!
  • @tester 一种方法是将 (150,150,3) 图像展平为矢量。但是,是的,这是 scikit learn 的一个限制,它不是训练图像模型的理想框架。
【解决方案2】:

这很困难,因为幕后的数学运算,(细节超出范围)如果你设法这样做,假设你构建了自己的算法,但你仍然不会得到想要的结果。 我曾经遇到过这个问题,面部大小不同。也许这段代码可以给你一个起点。

from PIL import Image
import face_recognition

def face_detected(file_address = None , prefix = 'detect_'):
    if file_address is None:
        raise FileNotFoundError('File address required')
    image = face_recognition.load_image_file(file_address)
    face_location = face_recognition.face_locations(image)

    if face_location:
        face_location = face_location[0]
        UP = int(face_location[0] - (face_location[2] - face_location[0]) / 2)
        DOWN = int(face_location[2] + (face_location[2] - face_location[0]) / 2)
        LEFT = int(face_location[3] - (face_location[3] - face_location[2]) / 2)
        RIGHT = int(face_location[1] + (face_location[3] - face_location[2]) / 2)

        if UP - DOWN is not LEFT - RIGHT:
            height = UP - DOWN
            width = LEFT - RIGHT
            delta = width - height
            LEFT -= int(delta / 2)
            RIGHT += int(delta / 2)

        pil_image = Image.fromarray(image[UP:DOWN, LEFT:RIGHT, :])
        pil_image.thumbnail((50, 50), Image.ANTIALIAS)
        pil_image.save(prefix + file_address)

        return True

    pil_image = Image.fromarray(image)
    pil_image.thumbnail((200, 200), Image.ANTIALIAS)
    pil_image.save(prefix + file_address)
    return False

注意:我很久以前写过这个可能不是一个好习惯

【讨论】:

  • 你启发了我制作一个 PIL 调整器脚本:github.com/dancrew32/image_shape_normalizer。我只是将所有图像转换为 RGB 并强制它们适合 100x100px 的大小。这使得所有 numpy.array 形状(100、100、3)。看到这有什么缺点吗?感谢您的启发
  • 实际上很难为这些图像获得正确的形状......发现这个 repo 是一个非常棒的工作流程,用于扫描构成标签和数据的目录目录:github.com/whimian/SVM-Image-Classification/blob/master/… 这些图像是并不完全相同,这很令人兴奋。
猜你喜欢
  • 1970-01-01
  • 2019-04-16
  • 1970-01-01
  • 2020-10-23
  • 2016-05-22
  • 2017-06-07
  • 2021-02-15
  • 2016-05-21
  • 2013-02-09
相关资源
最近更新 更多