Ubuntu 14.04 LTS で VIPS(8.1.1) を使用して、複数のスレッドを使用して多くの小さなタイルを読み取り、それらをまとめて大きな画像にする Python(3.4.3) プログラムを作成しています。
非常に単純なテストでは:
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Lock
from gi.repository import Vips
canvas = Vips.Image.black(8000,1000,bands=3)
def do_work(x):
img = Vips.Image.new_from_file('part.tif') # RGB tiff image
with lock:
canvas = canvas.insert(img, x*1000, 0)
with ThreadPoolExecutor(max_workers=8) as executor:
for x in range(8):
executor.submit(do_work, x)
canvas.write_to_file('complete.tif')
正しい結果が得られます。私の完全なプログラムでは、各スレッドの作業には、ソース ファイルからバイナリを読み取り、それらを tiff 形式に変換し、画像データを読み取り、キャンバスに挿入することが含まれます。動作しているように見えますが、結果を調べようとすると問題が発生しました。画像が非常に大きいため(〜50000 * 100000ピクセル)、画像全体を1つのファイルに保存できなかったので、試しました
canvas = canvas.resize(.5)
canvas.write_to_file('test.jpg')
これには非常に時間がかかり、結果の jpeg には黒いピクセルしかありません。サイズを 3 回変更すると、プログラムが強制終了されます。私も試しました
canvas.extract_area(20000,40000,2000,2000).write_to_file('test.tif')
これによりエラー メッセージsegmentation fault(core dumped)
が表示されますが、画像は保存されます。その中に画像コンテンツがありますが、間違った場所にあるようです。
何が問題なのだろうか?
以下は、完全なプログラムのコードです。OpenCV + sharedmem (sharedmem は multiprocessing 部分を処理) を使用して同じロジックも実装され、問題なく動作しました。
import os
import subprocess
import pickle
from multiprocessing import Lock
from concurrent.futures import ThreadPoolExecutor
import threading
import numpy as np
from gi.repository import Vips
lock = Lock()
def read_image(x):
with open(file_name, 'rb') as fin:
fin.seek(sublist[x]['dataStartPos'])
temp_array = np.fromfile(fin, dtype='int8', count=sublist[x]['dataSize'])
name_base = os.path.join(rd_path, threading.current_thread().name + 'tempimg')
with open(name_base + '.jxr', 'wb') as fout:
temp_array.tofile(fout)
subprocess.call(['./JxrDecApp', '-i', name_base + '.jxr', '-o', name_base + '.tif'])
temp_img = Vips.Image.new_from_file(name_base + '.tif')
with lock:
global canvas
canvas = canvas.insert(temp_img, sublist[x]['XStart'], sublist[x]['YStart'])
def assemble_all(filename, ramdisk_path, scene):
global canvas, sublist, file_name, rd_path, tilesize_x, tilesize_y
file_name = filename
rd_path = ramdisk_path
file_info = fetch_pickle(filename) # A custom function
# this info includes where to begin reading image data, image size and coordinates
tilesize_x = file_info['sBlockList_P0'][0]['XSize']
tilesize_y = file_info['sBlockList_P0'][0]['YSize']
sublist = [item for item in file_info['sBlockList_P0'] if item['SStart'] == scene]
max_x = max([item['XStart'] for item in file_info['sBlockList_P0']])
max_y = max([item['YStart'] for item in file_info['sBlockList_P0']])
canvas = Vips.Image.black((max_x+tilesize_x), (max_y+tilesize_y), bands=3)
with ThreadPoolExecutor(max_workers=4) as executor:
for x in range(len(sublist)):
executor.submit(read_image, x)
return canvas
上記のモジュール (mcv としてインポート) は、ドライバー スクリプトで呼び出されます。
canvas = mcv.assemble_all(filename, ramdisk_path, 0)
内容を調べるために、私は使用しました
canvas.extract_area(25000, 40000, 2000, 2000).write_to_file('test_vips1.jpg')