lisz-works

技術と興味の集合体

Python3 並べられた画像を分割する

【スポンサーリンク】

Python

Python3による画像編集についてです!

今回は並べられた画像を、分割して各画像ファイルとして出力する処理を作ってみました!

ソースはGitHubにもアップしてみました。

github.com

どんなことをするのか

例えばこんな感じで、適当なサイズのグリッド状に各画像がくっついたベース画像があるとします。

ベース画像

ゲームのマップチップとかみたいですね。

コレをえいやー!っとして

こんな感じでバラバラの連番ファイルで出力します。

バラバラに切り出した画像

画像分割するソースのサンプル

こんな感じで作ってみました。

コードを表示する

# -*- coding: utf-8 -*-
from PIL import Image
import os

imgName = 'img.png'
imgDir = os.getcwd() + '\\'

class ImgInfo:
    def __init__(self, name, sx, sy, ex, ey):
        self.name = name + '.png'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
    def carving(self):
        # 画像を切り出して保存
        baseFile = imgDir + imgName
        img = Image.open(baseFile)
        imgCrop = img.crop((self.sx, self.sy, self.ex, self.ey))
        output = imgDir + self.name
        imgCrop.save(output)
    def show(self):
        # 情報表示
        print('[{}] {: >4}, {: >4}, {: >4}, {: >4}'.format(self.name, self.sx, self.sy, self.ex, self.ey))

def getOfsList(num, size):
    ofs = []
    for i in range(0, num):
        ofs.append(i * size)
    return ofs

    # イメージの始点
    startX = 5
    startY = 5
    # イメージの幅、高さ
    imgW = 120
    imgH = 120
    #
    xofs = getOfsList(4, imgW)
    yofs = getOfsList(4, imgH)
    # 画像情報
    imgs = []
    cnt = 0
    ycnt = 1
    for y in yofs:
        xcnt = 1
        for x in xofs:
            name = str(cnt).zfill(2)
            sx = x + startX
            sy = y + startY
            ex = (imgW * xcnt) + startX
            ey = (imgH * ycnt) + startY
            img = ImgInfo(name, sx, sy, ex, ey)
            imgs.append(img)
            cnt += 1
            xcnt += 1
        ycnt += 1
    # 確認
    for img in imgs:
        img.show()
    # 分割
    for img in imgs:
        img.carving()

それでは詳細を解説していきます。

カレントフォルダの取得

最初に出てきた

import os

を使って、現在位置を取得します。

この2つのどちらかで、カレントフォルダをフルパスで取得することができます。

>>> os.path.abspath(".")
'C:\\work'
>>> os.getcwd()
'C:\\work'

このとき終端に「\\」が付きません。

なのでファイル名を繋げて、ファイルのフルパスを作りたいときは、手動で「\\」を付けてあげる必要があります。

インポートするライブラリ

インポートするのはこの2つです。

from PIL import Image
import os

PILというのが、画像処理を行う「Pillow」というライブラリです。
これは必須。

osは、ファイルパスを取得するために使用しています。

フォルダの移動

今回は使っていないので余談ですが、

>>> os.chdir("C:\\work\\python")

とすることで、フォルダを移動できます。

コレを実行して、カレントフォルダを表示すると

>>> os.getcwd()
'C:\\work\\python'

と表示されます。
ちゃんと移動していますね!

画像情報を扱うクラス

今回は画像情報を管理するクラスを作ってみました。

class ImgInfo:
    def __init__(self, name, sx, sy, ex, ey):
        self.name = name + '.png'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
    def carving(self):
        # 画像を切り出して保存
        baseFile = imgDir + imgName
        img = Image.open(baseFile)
        imgCrop = img.crop((self.sx, self.sy, self.ex, self.ey))
        output = imgDir + self.name
        imgCrop.save(output)
    def show(self):
        # 情報表示
        print('[{}] {: >4}, {: >4}, {: >4}, {: >4}'.format(self.name, self.sx, self.sy, self.ex, self.ey))

インスタンス生成時に、

  1. ファイル名(拡張子なし:png固定)
  2. 画像の開始座標 x, y
  3. 画像の幅、高さ w, h

を渡すことで、生成できます。

  1. carving(): 画像の切り出しをして保存
  2. show(): 情報表示

画像の切り出し処理と保存

ImgInfo.carving()では、画像の切り出しと保存を行っています。

切り出しと保存は3つのステップで行うことができます。

# 画像を開く
img = Image.open("開くファイル")
# 画像を切り出す
imgCrop = img.crop((始点X, 始点Y, 終点X, 終点Y))
# 切り出した画像を保存
imgCrop.save("保存するパス")

img.crop()は、あくまで始点座標と終点座標なのでお間違いなく。

画像の切り出し座標イメージ

x, y, w, hのつもりで設定するとエラーします。

ImgInfo.show() 情報表示

実行すると、こんな感じで出力されます。

[02.png] 240, 0, 360, 120

print内の「{}」は、format()で与えた変数の値が入ります。
左から順に対応してものが入ります。

「{: >4}」のように記述することで、「空白4つで右寄せ」を指定しています。

マス目の座標を生成する

マス目の座標をいちいち作るのが面倒……

ということで今回はこんな関数を作成しました。

def getOfsList(num, size):
    ofs = []
    for i in range(0, num):
        ofs.append(i * size)
    return ofs

マスの数とサイズを渡すと、各マスの座標が配列で返ってきます。

事前にスタート座標とサイズは設定してあるので

# イメージの始点
startX = 5
startY = 5
# イメージの幅、高さ
imgW = 120
imgH = 120

座標のオフセットを取得して

xofs = getOfsList(4, imgW)
yofs = getOfsList(4, imgH)

ループで処理することで、各座標が生成できます。

cnt = 0
ycnt = 1
for y in yofs:
    xcnt = 1
    for x in xofs:
        name = str(cnt).zfill(2)
        sx = x + startX
        sy = y + startY
        ex = (imgW * xcnt) + startX
        ey = (imgH * ycnt) + startY
        cnt += 1
        xcnt += 1
    ycnt += 1

全て座標を表示すると、こんな感じの状態になっています。

[00.png]    5,    5,  125,  125
[01.png]  125,    5,  245,  125
[02.png]  245,    5,  365,  125
[03.png]  365,    5,  485,  125
[04.png]    5,  125,  125,  245
[05.png]  125,  125,  245,  245
[06.png]  245,  125,  365,  245
[07.png]  365,  125,  485,  245
[08.png]    5,  245,  125,  365
[09.png]  125,  245,  245,  365
[10.png]  245,  245,  365,  365
[11.png]  365,  245,  485,  365
[12.png]    5,  365,  125,  485
[13.png]  125,  365,  245,  485
[14.png]  245,  365,  365,  485
[15.png]  365,  365,  485,  485

結果

最終的にこのように切り出し処理を行います。

for img in imgs:
    img.carving()

結果として、このような画像が

ベース画像

このように切り出されます。

バラバラに切り出した画像

成功ですね!

参考

これらを参考にしました。ありがとうございました。

note.nkmk.me

shuzo-kino.hateblo.jp

qiita.com

note.nkmk.me

note.nkmk.me

あとがき

ということで、Python3で並べられた画像を分割する処理についてでした!

ちょっと必要になったので作ってみたのですが、ある程度サクッとできたような気がします。

まとめて描いた画像を切り出したい!

とか

切り出し前提でまとめて描きたい!

なんてときに便利です。

あくまで座標計算と、APIの実行だけでできちゃいます。

なのでちょっと変えれば色々と応用できると思いますー!