TL;DR
手元にある画像を手っ取り早く高画質にするために、
手軽な方法を調べ、部分的に書き換えて二倍速にした。
はじめに
ちょうどこんな記事を見つけ、頑張れば良い感じの画像が手に入るであろうことは想像が付いていたので
まずは手軽にやる方法を探していました。
そして、見つけたのがRAISRを実装したリポジトリです。
結果の画像を見る限り、割と良い感じだったのでこれを採用して見た訳ですが、
前処理として使うには少々時間がかかり過ぎていたのでmultiprocessingで処理時間を短くしておきました。
RAISRの元論文もあるので、自分でゼロから始める人は、そちらを参考にして下さい。
リポジトリに載っていた結果の画像はこちらです。
ギザギザしていた部分が、綺麗になっています。
やったこと
test.pyで時間の掛かる処理をしていた部分を並列化しただけです。
元のコードで、何が不味いのかは一目見て分かることでしょう。
for row in range(margin, heightHR-margin):
for col in range(margin, widthHR-margin):
if round(operationcount*100/totaloperations) != round((operationcount+1)*100/totaloperations):
print('\r|', end='')
print('#' * round((operationcount+1)*100/totaloperations/2), end='')
print(' ' * (50 - round((operationcount+1)*100/totaloperations/2)), end='')
print('| ' + str(round((operationcount+1)*100/totaloperations)) + '%', end='')
sys.stdout.flush()
operationcount += 1
# Get patch
patch = upscaledLR[row-patchmargin:row+patchmargin+1, col-patchmargin:col+patchmargin+1]
patch = patch.ravel()
# Get gradient block
gradientblock = upscaledLR[row-gradientmargin:row+gradientmargin+1, col-gradientmargin:col+gradientmargin+1]
# Calculate hashkey
angle, strength, coherence = hashkey(gradientblock, Qangle, weighting)
# Get pixel type
pixeltype = ((row-margin) % R) * R + ((col-margin) % R)
predictHR[row-margin,col-margin] = patch.dot(h[angle,strength,coherence,pixeltype])
- 独立に計算しても問題のないものに対してシングルスレッドで処理を実行している
- pythonでforの二重ループを使っている
ぱっと見ですが、これを書き直すだけで一気に早くなりそうな気がします。
まずは、forの中身を関数化して抜き出します。
argwrapper
という関数はmultiprocessingで関数に引数を複数与えるために使います。
import multiprocessing
def argwrapper(args):
return args[0](*args[1:])
def calculateValues(numOfRows, upscaledLR, row, col):
if round(row * 100 / numOfRows) \
!= round((row + 1) * 100 / numOfRows):
print('|{sharps}{spaces}| {percent}%'.format(**{
'sharps': '#' * round((row + 1) *
100 / numOfRows / 2),
'spaces': ' ' * (50 - round((row + 1) *
100 / numOfRows / 2)),
'percent': round((row + 1) * 100 / numOfRows)
}), end='\r')
# Get patch
patch = upscaledLR[row - patchmargin: row + patchmargin + 1,
col - patchmargin: col + patchmargin + 1]
patch = patch.ravel()
# Get gradient block
gradientblock = upscaledLR[row - gradientmargin: row + gradientmargin + 1,
col - gradientmargin: col + gradientmargin + 1]
# Calculate hashkey
angle, strength, coherence = hashkey(gradientblock, Qangle, weighting)
# Get pixel type
pixeltype = ((row - margin) % R) * R + ((col - margin) % R)
return {
"row": row - margin,
"col": col - margin,
"value": patch.dot(h[angle, strength, coherence, pixeltype])
}
そして本題のmultiprocessingの部分です。
先ほど作成した関数の名前を第一引数にし、それ以降に関数の引数を列挙していくだけです。
RAISRの実装だと、行列の更新が必要だったので、最後にまとめて更新を行うことにしました。
poo = multiprocessing.Pool()
funcArgs = [(calculateValues, heightHR - margin,
upscaledLR, row, col)
for row in range(margin, heightHR - margin)
for col in range(margin, widthHR - margin)]
results = poo.map(argwrapper, funcArgs)
poo.close()
poo.join()
for result in results:
predictHR[result["row"], result["col"]] = result["value"]
multiprocessingを使うとこれだけで並列化ができてしまいます。
簡単ですね。
プログレスバーの部分はあまり気を使っていなかったため、少し雑な感じですが
本質ではないので、無視しています。
結果の確認
とある画像で、実行速度を比較して見ました。
まずは拾ってきたコードでの実行結果です。
- 315.3537690639496 [s]
- 309.78351306915283 [s]
- 360.8852210044861 [s]
次に、multiprocessingで並列化させたコードでの実行結果です。
- 138.09882402420044 [s]
- 138.4847867488861 [s]
- 140.33899188041687 [s]
4coreでやっているとは言え、流石に4倍速にはなりませんでしたが、2~3倍程度には速くなっていそうです。
画像の方も、問題なく綺麗になっていそうです。
速さにこだわりの持てそうな画像でやって見た場合も以下の様な感じでした。
- 拾ってきたコード
- 269.8946466445923 [s]
- 293.7288279533386 [s]
- 278.0401508808136 [s]
- 並列化したコード
- 139.08394598960876 [s]
- 133.12141489982605 [s]
- 138.94682788848877 [s]
まとめ
手軽に2倍速に出来た。
でも、もっと速くなって欲しい。