一定時間での画像の定性的削減

元の画像のサイズに比例して一定の時間動作し、予想外に高品質の結果が得られる、画像のサイズ変更の非常にシンプルで効果的な方法を共有したいと思います。 この方法は、あらゆる言語およびアプリケーションに適用できます。







まず、論理的に考えましょう。 画像のサイズを変更する場合、結果を少なくともリモートで元の画像に似せたいと思うでしょう。 これを行うには、元の画像から可能な限り多くの情報を考慮します。 「最近傍」方式について聞いたことがありますか? この方法では、最終画像の各点について、元の画像から1点だけが変更されずに取得されます。









最も近い隣人による4928×3280から256×170への画像縮小。







100%のスケールで、網膜のないブラウザーで記事の例を見ることをお勧めします。 つまり、表示するときにサイズ変更を最大限に除外します。

結果は良くありません。 画像は、けいれん的で粒子が粗く、その上に描かれているものを理解することさえ困難です。 特に、元の画像に多くの細かい部分がある場合、またはそれ自体が粗い場合。 これはなぜですか? 最終画像では、ソースからの情報はほとんど考慮されていないためです。 ソース画像で条件に応じて最終点に該当するポイントをマークすると、次のようなグリッドが得られます。









サイズが20×13の最終画像に含まれるポイント。



これで、元の画像に関する情報が最終的にどの程度少ないかを視覚的に評価できます。 同意します。ピクセルがこのグリッド上に正確に配置される可能性は低いため、元の画像のイメージがわかりやすくなります。







たとえば、畳み込みを使用してサイズ変更を行うと、まったく異なる結果が得られます。 その中で、ソース画像の各ピクセルは、最終的な、さらには複数回に貢献します。 したがって、画像は滑らかで、適切なフィルターを選択する場合はクリアします。









バイキュービックフィルターによる4928×3280から256×170の畳み込みへの削減。







それにもかかわらず、「最近接」方法には、1つの否定できない利点があります。元の画像のサイズに対して一定の時間動作します。 つまり、元の画像がどれだけ大きくても小さくても、特定のサイズへの縮小時間は同じになります。 Pillowライブラリを使用してPythonの例を示しますが、どの言語とライブラリを使用してもほぼ同じ結果を得ることができます。







>>> from PIL import Image >>> im = Image.open('pineapple.jpeg'); im.load(); im.size (2560, 1600) >>> %time im.resize((256, 170), Image.NEAREST) Wall time: 0.35 ms >>> im = Image.open('space.jpeg'); im.load(); im.size (4928, 3280) >>> %time im.resize((256, 170), Image.NEAREST) Wall time: 0.44 ms
      
      





実際には、プロセッサキャッシュやデータの局所性などのさまざまな要因が干渉するため、時間が一定ではありませんが、4倍のソースイメージであっても、わずか23%の速度低下しかありません。

反対に、畳み込み率は元の画像の増加とともに直線的に減少します。







 >>> from PIL import Image >>> im = Image.open('pineapple.jpeg'); im.load(); im.size (2560, 1600) >>> %time im.resize((256, 170), Image.BICUBIC) Wall time: 33.2 ms >>> im = Image.open('space.jpeg'); im.load(); im.size (4928, 3280) >>> %time im.resize((256, 170), Image.BICUBIC) Wall time: 130 ms
      
      





4倍のソースイメージの場合、時間も4倍に増加しました。







固定コア



グラフィックスを操作するためのいくつかのアプリケーションとライブラリーは、このようなトリックを使用します:コンボリューションサイズ変更と同じサイズ変更フィルターを使用するようです(たとえば、バイリニア、バイキュービック、およびランチョスフィルターがあります)が、画像を縮小する場合、フィルターコアは適応的に増加しません。 その結果、最終画像の任意のポイントを構築するために、元画像の4ピクセルのみがバイリニアフィルター、バイキュービックフィルター、16、3フロントランチョスフィルター、36で使用されます。つまり、動作時間も元のサイズに対して一定であることがわかります。







しかし、このアプローチは、それを約2倍だけ減らすように機能するだけであり、その結果は「最近接」とあまり変わりません。









固定コアバイリニアフィルターを使用した4928×3280〜256×170。







また、「「最近傍」とそれほど変わらない」と言うことで、ぼんやりと粗いというわけではなく、「最近傍」の結果にほぼ一致するということです。 隣接するブラウザタブで両方の画像を開き、それらを切り替えると、画像はほぼ一致します。 4ピクセルが固定コアで補間されており、最初のピクセルはバカにされておらず、結果は元のピクセルに近いため、エラーが発生する可能性があります。 ただし、ここにはエラーはありません。その理由は次のとおりです。









20×13に縮小したときに補間されるポイント。







これらは、ファイナルが構築されるソースイメージのポイントです。 それらは4倍ありますが、すべて最近傍法と同じ場所にあります。 つまり、ほとんどの場合、画像に関する新しい情報は受信されません。 バイキュービックフィルターを適用することで、プロセスに関与する元の画像のピクセル数を増やすことができますが、バイキュービックフィルターでは極端なピクセルが負の係数で取得されるため、結果は再びほぼ同じになり、さらに破れます。









4928×3280から256×170まで、固定コアのバイキュービックフィルター付き。







推測するのは難しくありません。大きなカバレッジのフィルターを使用する場合の複雑さと実行時間は大幅に増加しますが、最終的なイメージはほとんど変化しません。 次の3つの例はすべてほぼ同じ図を示していますが、動作時間は最大20倍異なります。







 >>> im = Image.open('space.jpeg'); im.load(); im.size (4928, 3280) #   >>> %time im.resize((256, 170), Image.NEAREST) Wall time: 0.441 ms #    >>> %time im.transform((256, 170), Image.AFFINE, (im.width / 256, 0, 0, 0, im.height / 170, 0), Image.BILINEAR) Wall time: 3.62 ms #    >>> %time im.transform((256, 170), Image.AFFINE, (im.width / 256, 0, 0, 0, im.height / 170, 0), Image.BICUBIC) Wall time: 9.21 ms
      
      





ここでは、アフィン変換を使用して固定カーネルのサイズ変更をシミュレートしました。 しかし、いくつかのアプリケーションとライブラリは実際にこれを行います。それらはより高価なフィルターを使用してそれらを削減し、その結果は最近傍法とほぼ同等です。 これがOpenCVの機能です。したがって、キャンバスは画像をキャンバスに描画するときにブラウザが実行するため、ミップレベルなしでテクスチャリングする場合はビデオカードも実行します。 少なくとも時間が長いが、元の画像の解像度に対して一定であるためです。 さて、品質はどうですか? 品質には畳み込みがあります。







修正方法



なぜ私がこれをすべて言っているのかと思うかもしれませんが、それは明らかです。速度が必要な場合は、品質が畳み込みである場合、「隣人」または固定コアを取る必要があります。 しかし、実際には、結果は根本的に改善されるように、固定コアの減少を修正することができます。 あなたのタスクが可能であるほどはるかに良い、これは十分であり、畳み込みは必要ありません。 さらに、複雑さは元のイメージのサイズに対して一定であるだけでなく、固定カーネルを使用する場合と同じ定数になります。









一定時間で4928×3280の結果を256×170にサイズ変更します。







ご覧のとおり、このアルゴリズムの結果は、「最近接」または固定コアの後に取得されたマルチカラーの混乱と比較することはできません。 この記事の例として、私は意図的に細かいグリッドのあるかなり大きな写真を撮り、多くの詳細(宇宙飛行士のヘルメットの反射を見てください)を大幅に減らしました。 可能な限り多くのアーティファクトを取得するために最善を尽くしましたが、アルゴリズムはまだ対応しています! random1stからこの方法を初めて知ったとき、関与するピクセルの数は同じであるため、この方法では固定コアと比較してわずかな改善しか得られないと考えました。 しかし、結果は私の期待を大きく上回りました。







秘密は、固定コアのように4つにノックされたポイントではなく、最終的な2倍の解像度の均一なグリッドを処理することです。 そして、そこからすでに最終画像を補間しています。









20×13に減少するときに使用されるポイント。







ご覧のとおり、元の画像のかなりの数の点がまだ撮影されています。 しかし、それらは均等に分布しているという事実により、より代表的です。 そして、それらの正確に4倍の数があるという事実のために、それらはすべて最終的なイメージに同じ貢献をします。







そして今、最も興味深いのは、このメソッドを使用するために何もプログラムする必要がないということです! 必要なのはすでにそこにあります。 最初のステップは、「最近傍」法を使用してピクセルの均一なグリッドを2倍高い解像度にすることです。2番目のステップでは、少なくとも固定フィルター、少なくとも畳み込み、少なくともボックスフィルター(ライブラリの内容によって異なります)で2倍に圧縮します。 たたみ込みのための唯一のものは、ハミングフィルターまたはバイキュービックを使用することをお勧めしますが、バイリニアではありません。







 >>> im = Image.open('space.jpeg'); im.load(); im.size (4928, 3280) #      >>> %time im.resize((512, 340), Image.NEAREST)\ .transform((256, 170), Image.AFFINE, (2, 0, 0, 0, 2, 0), Image.BILINEAR) Wall time: 3.59 ms #       >>> %time im.resize((512, 340), Image.NEAREST)\ .resize((256, 170), Image.HAMMING) Wall time: 2.42 ms #       >>> %time im.resize((512, 340), Image.NEAREST)\ .resize((256, 170), Image.BICUBIC) Wall time: 3.53 ms #   - #    ,    OpenCV #      INTER_NEAREST # . https://github.com/opencv/opencv/issues/9096 >>> import cv2 >>> im = cv2.imread('space.jpeg') >>> %time cv2.resize(cv2.resize(im, (512, 340), interpolation=cv2.INTER_NEAREST), (256, 170), interpolation=cv2.INTER_AREA) Wall time: 0.81 ms
      
      





アイデアのさらなる発展



この改善は印象的ですが、そこで止めることはできません。 誰がそれを構築するには、ちょうど2倍の大きな画像を使用する必要があると言いましたか? より良い品質を得るために、3回または4回はいかがですか 確かに、2番目のステップで固定カーネルでresizeを使用することは不可能です。なぜなら、私たちが取り除こうとしているのと同じ問題が出てくるからです。 しかし、畳み込み-お願いします。 この場合、時間は一定に保たれ、定数だけが長くなります。









2xおよび4xの中間画像を使用して、4928×3280から256×170にサイズ変更します。







おそらくこの規模では、違いはそれほど目立ちませんが、かなり強いものです。 それらに気付くには、ズームでgifを見てください:













さて、時間:







 >>> im = Image.open('space.jpeg'); im.load(); im.size (4928, 3280) #   2x   >>> %time im.resize((512, 340), Image.NEAREST)\ .resize((256, 170), Image.BICUBIC) Wall time: 3.53 ms #   3x   >>> %time im.resize((768, 510), Image.NEAREST)\ .resize((256, 170), Image.BICUBIC) Wall time: 6.27 ms #   4x   >>> %time im.resize((1024, 680), Image.NEAREST)\ .resize((256, 170), Image.BICUBIC) Wall time: 9.23 ms
      
      





ご覧のとおり、2倍の中間画像を使用するオプションは、固定コアのバイリニアフィルターとほぼ同じ時間動作し、4倍の中間画像を使用するオプションはバイキュービックフィルターを使用します。 まあ、一般的に言えば、あなたはポイントの整数を使用することはできません。







正しい選択の仕方



問題は、この方法がより良い結果をもたらし、固定コアの速度で機能する場合、なぜ固定コアを使用してそれを減らすのかということです。 もちろん、この方法にはさまざまな適用性があります。2倍未満に削減する場合は、この方法を使用しない方が良いでしょう。 そして、これは固定コアの適用限界と一致します。2倍以上削減する場合は使用しないほうが良いです。 方法を組み合わせることで、任意の規模で一定時間、許容可能な品質のサイズ変更を取得できることがわかりました。







重要な追加



コメントでは、 ヴィンテージはこのメソッドがスーパーサンプリングと呼ばれていることを正しく示しています。 スーパーサンプリングは、エイリアシングを排除するためにゲームでよく使用されます。 実際、ゲームシーンは任意の解像度で描画できるため、無限の解像度の画像です。 スーパーサンプリングの場合、シーンは必要以上に高い解像度で描画され、いくつかの隣接するピクセルは1つに平均化されます。 つまり、類推は完全です。 しかし、これはそのような方法がその利点にもかかわらずソフトウェアで非常にまれに使用されるという事実を否定しません。









最後に、他の画像を使用したいくつかの例を示します。 左から右へ:

1)固定コア、バイリニアフィルター(現在多くのユーザーが使用しているもの)

2)参照としてのバイキュービック畳み込み

3)倍率2倍のスーパーサンプリング

4)4倍の倍率でのスーパーサンプリング







表示する際に覚えておくべき主なことは、3番目の画像は1番目の画像とまったく同じ時間に生成され、4番目の画像は約3倍長くなりますが、一定時間であり、多くの場合2番目の画像よりも最大20倍速いことです。







もう一度、記事の例。 画像4928×3280は19.25倍に縮小されました。









同じ画像ですが、今回は600×399から2.34倍に減少しました。

このような小さな減少は、この方法ではより複雑なケースです。









2560×1600の画像は 10倍に縮小されました。









4000×2667の画像は 15.625倍に縮小されます。









9.5625倍に縮小された2448×3264の画像。









2000×2000画像は7.8125倍に縮小されました。










All Articles