OpenCV – 画像を格子状に分割する方法

目次

概要

OpenCV で読み込んだ画像を格子状に分割して複数枚の画像にする、また同じサイズの複数枚の画像を格子状に並べて 1 枚の画像にする方法について解説します。

1 枚の画像を格子状に分割して複数枚の画像にする

sample.jpg

行数、列数を指定して、分割する場合

OpenCV で読み込んだ画像は 2 次元または 3 次元配列なので、numpy.array_split() を使用して、分割します。

画像と axis の対応

まず indices_or_sections=行数, axis=0 として、画像を行方向に分割します。

In [1]:
# hide_cell
import cv2
import numpy as np
from IPython.display import Image, display


def imshow(img):
    """numpy 配列を Notebook 上に表示する。"""
    encoded = cv2.imencode(".jpg", img)[1]
    display(Image(encoded))


img = cv2.imread("sample.jpg")

rows = 5  # 行数
cols = 7  # 列数

shreds = []
for i, shred in enumerate(np.array_split(img, rows, axis=0)):
    if i != 0:
        shred = np.pad(shred, ((5, 0), (0, 0), (0, 0)), constant_values=255)
    shreds.append(shred)

imshow(np.concatenate(shreds, axis=0))

次に indices_or_sections=列数, axis=1 として、画像を列方向に分割します。

行数を 5、列数を 7 としたので、全部で 35 枚の画像に分割されました。 分割した画像は output ディレクトリを作成して、そこに保存します。

In [1]:
import cv2
import numpy as np
from IPython.display import Image, display


def imshow(img):
    """numpy 配列を Notebook 上に表示する。"""
    encoded = cv2.imencode(".jpg", img)[1]
    display(Image(encoded))
In [2]:
from pathlib import Path

import cv2
import numpy as np


def split_img(rows, cols):
    chunks = []
    for row_img in np.array_split(img, rows, axis=0):
        for chunk in np.array_split(row_img, cols, axis=1):
            chunks.append(chunk)

    return chunks


img = cv2.imread("sample.jpg")

chunks = split_img(rows=5, cols=7)
print(len(chunks))

# 保存する。
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
for i, chunk in enumerate(chunks):
    save_path = output_dir / f"chunk_{i:02d}.png"
    cv2.imwrite(str(save_path), chunk)
35

分割後の画像の大きさを指定して分割する場合

分割後の画像の大きさから、行数、列数を逆算します。

  • 行数 = 画像の高さ / 分割後の画像の高さ
  • 列数 = 画像の幅 / 分割後の画像の幅

除算の結果は、端数を切り捨てる場合は numpy.floor()、切り捨てない場合は numpy.ceil() を使って整数にします。

それ以外の部分は先のコードと同じです。

In [3]:
from pathlib import Path

import cv2
import numpy as np


def split_img(size):
    rows = int(np.ceil(img.shape[0] / size[0]))  # 行数
    cols = int(np.ceil(img.shape[1] / size[1]))  # 列数

    chunks = []
    for row_img in np.array_split(img, rows, axis=0):
        for chunk in np.array_split(row_img, cols, axis=1):
            chunks.append(chunk)
    print(len(chunks))


img = cv2.imread("sample.jpg")

chunk = split_img(size=(64, 64))

# 保存する。
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
for i, chunk in enumerate(chunks):
    save_path = output_dir / f"chunk_{i:02d}.png"
    cv2.imwrite(str(save_path), chunk)
20

同じサイズの複数枚の画像を格子状に並べて 1 枚の画像にする

同じサイズの複数枚の画像を格子状に並べて 1 枚の画像にするコードを紹介します。

今回は例として、sklearn.datasets.fetch_openml() で大きさが (28, 28) の 100 枚の手書き数字画像を 10 行 10 列の格子状に並べて、1 枚の画像にします。

In [4]:
from sklearn.datasets import fetch_openml

# ダウンロードが発生するので、実行に少し時間がかかります。
mnist = fetch_openml("mnist_784", version=1)
imgs = mnist["data"].values.reshape(-1, 28, 28).astype(np.uint8)[:100]
print(imgs.shape)
(100, 28, 28)
In [5]:
def concat_grid(imgs, cols, background=(255, 255, 255), gap=5):
    rows = int(np.ceil(len(imgs) / cols))

    # 出力画像の大きさを計算する。
    cell_w, cell_h = imgs[0].shape[1], imgs[0].shape[0]
    img_w = cell_w * cols + gap * (cols - 1)
    img_h = cell_h * rows + gap * (rows - 1)
    dst = np.full((img_h, img_w, 3), background, dtype=np.uint8)

    # 画像を配置する。
    for i, img in enumerate(imgs):
        row = i // cols
        col = i % cols
        x = (cell_w + gap) * col
        y = (cell_h + gap) * row

        if img.ndim == 2:
            img_bgr = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        dst[y : y + img.shape[0], x : x + img.shape[1]] = img_bgr

    return dst


dst = concat_grid(imgs, cols=10, gap=3)
imshow(dst)

コメント

コメントする

目次