Python 3.5 から Potrace を使い、ラスタ画像をベクタに変換したい。
より詳細に言うと、 OpenCV 3 の Python Bindings の cv2.imread() で読み込んだ画像データ(numpy.ndarray)をSVGデータに変換したかった。
pypotrace ってのもあるけど 3.5 で使えるのか分からなかったので見送り。
python から potrace 使ってる例がないかと探してみたらあった。
ここではパイプで標準入力から入力し、パイプで標準出力に出力している。
今回はこのサイトのやり方を元にした。
実装
Potrace は、標準入力からファイルを受け取ると、出力を標準出力に吐くとのことである。
If no filename arguments are given, then potrace acts as a filter, reading from standard input and writing to standard output. A filename of "-" may be given to specify reading from standard input.
Man page for potrace(1)
明示的に標準入力から読み込む場合はファイル名に "-" を、標準出力に出力する場合は "-o-" を指定する。
さて、 potrace が入力を受け付けるのは pnm (pbm, pgm, ppm), bmp だけである。今回は bmp を使う。
まずは cv2.imread() で読み込んだ画像データを bmp のバイナリデータにする。 cv2.imencode() は numpy.ndarray() を返すので .toarray() で bytes 型に変換している。
pic = cv2.imread('test.png', cv2.IMREAD_GRAYSCALE) retval, buf = cv2.imencode('.bmp', image) binbmp = buf.tobytes()
subprocess.Popen クラスでパイプを作成し、 .communicate() メソッドに input を指定して potrace の標準出力にBMPのバイナリを流し込む。問題がなければ stdout に potrace から出力されたSVGのbytesデータが入る。
p = subprocess.Popen( ['potrace', '-', '-o-', '--svg'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate(input=binbmp)
コード
こんな感じになった。
#!/usr/bin/env/python3 # coding: utf-8 import numpy as np, cv2, subprocess # potrace command POTRACE = 'potrace' def passpotrace(image): # convert to bmp binary so that potrace can handle it retval, buf = cv2.imencode('.bmp', image) if retval == False: raise ValueError('Failed to convert into BMP binary data') # convert buf from numpy.ndarray to bytes binbmp = buf.tobytes() args = [ POTRACE, '-', '-o-', '--svg' ] p = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False ) stdout, stderr = p.communicate(input=binbmp) if len(stderr) != 0: raise RuntimeError('Potrace threw error:\n' + stderr.decode('utf-8')) return stdout if __name__ == '__main__': testpic = cv2.imread('test.png', cv2.IMREAD_GRAYSCALE) print(passpotrace(testpic).decode('utf-8'))