Numpyで分割サイズを指定して画像を均等にグリッド化させる

前書き

大きめの画像から物体認識を実装する際、
やはり画像をグリッドごとに分割すべきです。
そんな時画像のピクセル数を指定して分割したいと思ったので、
記事を書いてみました。

画像処理ライブラリ「Pillow」

Pillowとは画像処理ライブラリで、
対抗馬になりうるのはOpenCVです。
どちらも使ってみましたが、ドキュメントを読みながら書いていく感じです。

やりたかったこと

大きめの画像をn等分や余りなく等分するスクリプトはよく見るが、
pxを指定しながら分割し、余りが存在する場合は余りのサイズで分割する
グリッド化は余りみない気がしたので、自分で書きました。

f:id:electric-city:20201015233335p:plain
画像全体

f:id:electric-city:20201015233340p:plain
グリッド化

上記だと黄色の部分は300px×300pxで、
基本的には黒色の画像全体を300pxの正方形でグリッド化したかったのです。
しかし余りが発生するのでそこはサイズに応じて分割しました(オレンジ・茶・赤)。

実際のコード

PILで読み込んだ画像を、
配列操作しやすくするためにNumpy配列に変換し、再びPILに変換しています。

import os
import sys
import math
import numpy as np
from PIL import Image

# 300pxずつ分割したい
separate_w, separate_h = 300, 300

# 分割関数
def split_image_unequal(w, h, img, div_w, div_h, filename):
    # 幅の分割矩形数分だけ
    for i in range(w//div_w+1):
        # 矩形の幅
        sep_w = div_w
        #   余りが発生した時は余った分のサイズで
        if i == w//div_w : sep_w = (w-(w//div_w)*div_w)-1

       #  高さの分割矩形数分だけ
        for j in range(h//div_h+1):
           
   # 矩形の高さ
            sep_h = div_h
            #   余りが発生した時は余った分のサイズで
            if j == h//div_h : sep_h = (h-(h//div_h)*div_h)-1
            #  numpyArrayなのでスライス(w,h,channelの3次元)
            sep_img = img[div_w*i:div_w*i+sep_w+1, div_h*j:div_h*j+sep_h+1, :]
            
            # PILに変換
            pil_img = Image.fromarray(sep_img)
            
            # ファイルに保存
            pil_img.save('./image/separate/%s_%d_%d.jpg'%(filename, i+1, j+1))
            print('i: %d, j:  %d,[縦: (%d %d), 横: (%d %d)]'%(i,j,div_w*i,div_w*i+sep_w,div_h*j,div_h*j+sep_h))

def separate(img, w, h, filename):
    print('width: %d, height: %d'% (int(w), int(h)))
 # 関数には画像幅、画像高さ、画像(Numpy配列)、矩形幅、矩形高さ、ファイル名
    return split_image_unequal(w, h, img, separate_w, separate_h, filename)

使い方

# ディレクトリ内の画像ファイルパス(string)
for file in files:
            #  ファイルパスを読み込んでPIL変換
            img = Image.open(file)
            
            # ファイル名(拡張子の前を抽出)
            ftitle = img.filename
            ffile = ftitle.split('/')[-1]
            fname = ffile.split(".")[-2]
            print(fname)
            
            # Numpy配列化
            img_np = np.array(img)
            w,h = img_np.shape[0], img_np.shape[1]
            # separate関数には画像(Numpy配列)、幅、高さ、ファイル名
            separate_img = separate(img_np, w, h, fname)

リンク

github.com