こんな画像があるとする。

今回はこれを

こういう風にパーツに分割したい、という時のコード。

# coding: utf-8
import cv2
from PIL import Image, ImageDraw
from functools import reduce

filename = 'shapes.png'

# 外側の輪郭を取得
def find_contours(name):
    im = cv2.cvtColor(cv2.imread(name), cv2.COLOR_BGR2GRAY)
    _, im_bw = cv2.threshold(im, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    contours, _ = cv2.findContours(im_bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    return contours

image = Image.open(filename)
contours = find_contours(filename)

for i, contour in enumerate(contours):
    # 輪郭データからpolygonに渡すための変換(もっと良い方法があるかもしれない)
    area = tuple(map(lambda c: tuple(c[0]), contour.tolist()))
    # マスク画像を作成
    mask = Image.new("L", image.size, 0)
    ImageDraw.Draw(mask).polygon(area, fill=255)
    copy = image.copy()
    copy.putalpha(mask)
    # 切り抜き
    bbox = reduce(lambda p, c: [
            min(p[0], c[0]), min(p[1], c[1]),
            max(p[2], c[0]), max(p[3], c[1])
        ], area, [image.width, image.height, 0, 0])
    output = copy.crop(bbox)
    output.save('shape_' + str(i+1) + '.png')

まずは、openCVで輪郭を抽出します。
抽出した情報を元にマスク画像(白黒画像)を作成。
そのマスク画像を使って元画像のコピーにアルファチャンネルを追加し、
外接する矩形で切り取って保存する。
という感じで実現できました。

ソースのコメントにも書いてあるとおり、contoursからtupleに切り替えるところがもっとスマートに書けると良いのだけれど、
まあ、とりあえず動いたので良しとします。

参考