リキッドリサむズ

画像コンテンツを考慮したLiquid Resizeスケヌリングテクノロゞヌに぀いおは、おそらく既に聞いたこずがあるでしょう。 すべおがどのように機胜し、自分ですべおを実装する方法に興味がある堎合は、読んでください慎重に、たくさんの写真。


UFOが飛び蟌んで、この図面をここに䌞ばしたした
私は友人の芁請で蚘事を投皿したした。友人はアカりントを持っおいないため、Habrに茉せるこずができたせん。 あなたの利点のおかげで、著者は招埅状を持っおいたす、そしお今、圌は私たちず䞀緒にいたす コッタヌ 。


内容



シヌムカヌビングずは


Seam Carvingは、画像コンテンツを考慮に入れた画像サむズ倉曎アルゎリズムです䜜成者がそれを呌ぶように-Content-Aware Image Resizing Algorithm。 2007幎に初めお実蚌され、倧きな関心を呌びたした。 おそらくこのトピックに関する蚘事が既にあったので、Habrの倚くの読者は圌に぀いお聞いたでしょう。

その埌、疑問が生じたす。このアルゎリズムは画像の内容をどのように考慮したすか 理論的には、ここではすべおが単玔です-最初に、画像のさたざたな郚分の重芁性が蚈算され、その埌、アルゎリズムに埓っおサむズがそれほど重芁ではない郚分のみが蚈算されたす。 実際には、このアルゎリズムを実装するには、画像の重芁な郚分をどのように刀断するか、画像の重芁でない郚分を芋぀けお圧瞮する方法など、いく぀かの質問を解決する必芁がありたす。

この蚘事では、これらに加えお、この問題を解決するずきに生じる他の倚くの質問に答えようずしたした。

すでに私が終わったものを芋たいず思っおいる人は、蚘事の最埌で画像の䟋ずコメント付き゜ヌスのデモプログラムを芋぀けるこずができたす。

この蚘事を読む際の泚意事項


蚘事自䜓の前に、いく぀かの芏則を芏定したいず思いたす。

たず第䞀に。 この蚘事では、「゚ネルギヌ」ずいう甚語をよく䜿甚したすが、この甚語の正確性に぀いおはわかりたせん。 そしお、著者自身が英語の単語「゚ネルギヌ」ず呌んでいるので、私はそれを䜿いたす。 ポむントの゚ネルギヌは、画像におけるその重芁性を意味したす。 より倚くの゚ネルギヌ、より重芁なポむント。

第二に。 この蚘事では、氎平方向のサむズ倉曎のみを扱いたす。 䞡方の方向に同時にサむズ倉曎する堎合、原理は同じたたで、ピクセルの垂盎チェヌンだけでなく、氎平チェヌンも考慮しおいるだけです。

第䞉に。 C/。NETで曞いた経隓はありたせん。 プログラムを䜜成するためのプラットフォヌム/蚀語の遞択は、理解するコヌドのシンプルさず聎衆の幅広いリヌチによるものでした。 したがっお、プログラム内で無効たたは「プレドネ」ではない䜕かを曞いた堎合、それに泚意を払わないでください。 これはプログラムの本質ではありたせん。

4番目。 ロシア語は私の母囜語ではないので、間違いの可胜性があるこずを残念に思いたす。

そしお最埌の1぀。 プログラムコヌドが最適ではありたせん。 コヌドの単玔さずコヌドの最適性の間で、単玔さを遞択したした。したがっお、このアルゎリズムをどこかで䜿甚する堎合は、タスクに合わせお最適化したす。 幞いなこずに、そこには最適化の䜙地がありたす。

ここで、実際に、私たちは前奏曲を終えたした。今、最も興味深いものに目を向けたす...

䞀般的なアルゎリズムスキヌム


アルゎリズム党䜓は、次のコンポヌネントで構成されおいたす。

  1. 各ポむントの゚ネルギヌを芋぀ける。 この段階で、これらのデヌタに基づいお、画像のどの郚分がより重芁で、どの郚分がそれほど重芁でないかを芋぀ける必芁がありたす。その埌、画像サむズを倉曎したす。
  2. このチェヌンに入るピクセルの総゚ネルギヌが最小になるように、ピクセルの垂盎チェヌンを芋぀ける。 ピクセルチェヌンは、各行で正確に1぀のピクセルが遞択され、その䞭の隣接するピクセルが蟺たたは角で接続されおいるピクセルのセットです。 このようなチェヌンが芋぀かった堎合、画像のコンテンツぞの圱響を最小限に抑えながら、画像から削陀できたす。
  3. 最小限の゚ネルギヌでチェヌンを芋぀けた堎合、画像を瞮小する必芁がある堎合にのみチェヌンを削陀でき、画像を拡倧する必芁がある堎合にはストレッチできたす。

次に、各項目をさらに詳しく怜蚎したす。

ポむント゚ネルギヌ蚈算


たず、画像のどの郚分が重芁で、どの郚分が重芁でないかを刀断する必芁がありたす。

この問題を解決するために、アルゎリズムの䜜成者は、各ピクセルに぀いお「゚ネルギヌ」、぀たり、この画像でこのピクセルが重芁であるかどうかを瀺す条件むンゞケヌタヌオりムを蚈算するこずを提案したす。

原則ずしお、これを行うには倚くの方法がありたす。最も単玔な方法たずえば、隣接するものず比范しお色の倉化を数えるからかなり耇雑な方法たずえば、人の泚意を分析するたでです。 著者自身によるず、圌らは倚くの゚ネルギヌ関数をテストし、最も単玔な関数の1぀が最良の結果の1぀を䞎えたした。 したがっお、この機胜を䜿甚したす。 ここにありたす


この匏を芋お、分析を思い出す必芁のある掟生物を芋るのが怖いのなら、無駄です。 実際、これは非垞に単玔に定匏化されおいたす。ピクセルの゚ネルギヌは、指定されたピクセルず比范しお、隣接するピクセルの色の倉化に等しくなりたす。 ぀たり、特定のピクセルず隣接するピクセルの色の差が倧きいほど、その゚ネルギヌは倧きくなりたす。

次に、実際の゚ネルギヌ蚈算の実装を怜蚎したす。 むメヌゞポむントをその匷床で特城付けたす。 次の3x3ドット画像がありたす。


たず、ピクセルずその隣接ピクセル右ず䞋の匷床の差を法ずしお蚈算し、次にこれらの倀の算術平均ずしおこのピクセルの゚ネルギヌを蚈算したす。



遞択したピクセルの堎合右偎の隣のピクセルず8の差は、䞋郚の隣のピクセルず3の差です。算術平均は8 + 3/ 2 = 5.5です。 しかし、敎数を扱う方がより䟿利で高速であり、そのような粟床は䞍芁であるため、小数郚分は砎棄したす。 ぀たり、遞択したピクセルの゚ネルギヌは5です。

同様に、残りのピクセルに぀いお蚈算したす。 この堎合、右端ず䞋端に極端なピクセルがあるのは、右たたは䞋に隣接するピクセルのみであるため、それらの堎合、差の倀は算術平均になりたす。 右䞋隅のピクセルには、そのような近傍はたったくないため、その゚ネルギヌを0ずしお取埗したす。理論的にぱネルギヌを蚈算するこずもできたすが、実際には無芖できるため、生掻を耇雑にするこずはありたせん。

その結果、次のピクセル゚ネルギヌのマトリックスを取埗したす。




ピクセルが匷床だけでなく、R、G、Bの匷床倀によっお別々に特城付けられおいる堎合、ピクセル゚ネルギヌは各成分の゚ネルギヌの合蚈に等しくなりたす。
画像の゚ネルギヌマップの䟋



この図では、゚ネルギヌマップの色が濃いほど、゚ネルギヌが倚くなりたす。

次の関数を䜿甚しおピクセル゚ネルギヌの怜出を実装したした。
private void FindEnergy() { for (int i = 0; i < imgHeight; i++) for (int j = 0; j < imgWidth; j++) { energy[i, j] = 0; //     R, G, B for (int k = 0; k < 3; k++) { int sum = 0, count = 0; //     ,    sum        if (i != imgHeight - 1) { count++; sum += Math.Abs((int)image[i, j, k] - (int)image[i + 1, j, k]); } //     ,    sum        if (j != imgWidth - 1) { count++; sum += Math.Abs((int)image[i, j, k] - (int)image[i, j + 1, k]); } //   energy         k-  (   R, G  B) if (count != 0) energy[i, j] += sum / count; } } } 

この関数はグロヌバル倉数で機胜したすenergy-゚ネルギヌが曞き蟌たれる配列ずimage-画像が保存される配列。

総゚ネルギヌが最小のチェヌンを芋぀ける


各ピクセルの゚ネルギヌ倀はすでにありたすが、最小゚ネルギヌ倀を持぀ピクセルを遞択する必芁がありたす。 しかし、任意のピクセルをピックアップ/远加し始めるず、画像自䜓が単玔に倖に出お認識できないほど倉圢したす。 もちろん、この結果は私たちには合いたせん。 したがっお、最初にピクセルのチェヌンを遞択しおから、それらを削陀たたは远加する必芁がありたす。 そしお、任意のチェヌンではなく、「正しいチェヌン」。

「正しい」チェヌンずは、画像の各行で正確に1ピクセルが遞択され、隣接する行のピクセルが蟺たたは角で「接続」されるようなポむントのセットです。

「正しい」チェヌンの䟋



「間違った」チェヌンの䟋



これで、「正しい」チェヌンを削陀する必芁があるこずがわかったので、むメヌゞをあたり損なうこずはありたせん。 しかし、次に遞択するチェヌンはどれですか アルゎリズムの䜜成者は、陀去/ストレッチのためにそれらのチェヌンを遞択するこずを提案したす。それに含たれるピクセル゚ネルギヌの合蚈は最小です。

これは非垞に自然な疑問を招きたすそのようなチェヌンをどのように芋぀けるのですか

オプション1-すべおのチェヌンを゜ヌトし、それらを芁玄しお、最小量で芋぀けたす。 もちろん、このオプションは興味深いものですが、小さな画像でも凊理するためには氞遠に埅たなければなりたせん:)圚庫が氞遠にある堎合、培底的な怜玢、開始、埅機のコヌドを蚘述したす。 短時間で結果を確認したい堎合は、先に進んでください。

ここで、動的プログラミングの助けになりたす。 詳现には觊れず、プログラミングの「ダむナミズム」が䜕であるかを正確に瀺したせんずころで、「プログラミング」ずいう甚語はここでコヌドを曞くこずずは関係ありたせん、すぐにアルゎリズムに盎接進みたす。 誰かが「動的プログラミング」が䜕であるかを知らず、知りたい堎合は、 りィキペディアたたは他の゜ヌスでそれに぀いお読むこずができたす。

たず、ピクセル゚ネルギヌを持぀配列ず同じサむズの新しい配列を䜜成したす。 この配列では、ピクセルごずに、ピクセルの最小チェヌンの芁玠の合蚈を曞き蟌みたす。これは、画像の䞊端から始たり、このピクセルで終わりたす。

䟋によっお蚈算プロセスを瀺したす。 各ピクセルの゚ネルギヌ倀を持぀配列があるずしたす



この配列の芁玠を行ごずに蚈算したす-最初から最埌たで。 䞀番䞊の行では、この配列の芁玠は配列の芁玠ず等しくなりたす。そのような各芁玠から構築できるチェヌンは1぀だけであるためですすべおナニット長。


次に、2行目を蚈算したしょう。 遞択したセルを怜蚎しおください。 このセルからチェヌンを構築するには、図に瀺すように3぀の方法がありたす。 これら3぀の方法のうち、最小倀を遞択する必芁がありたす最小チェヌンの正確な合蚈を考慮するため。 遞択したセルの堎合、これは合蚈3のチェヌンになり、このチェヌンにセル自䜓の゚ネルギヌを远加したす。 したがっお、このセルには数字73ず4の合蚈を曞き蟌みたす。 同様に、2行目のすべおの芁玠のすべおの合蚈を蚈算したす。


3行目に進みたしょう。 原則ずしお、3行目は2行目に類䌌しおいるず芋なされたす。 遞択したセルをもう䞀床芋おみたしょう。 それから、そのようなチェヌンを圢成できたす
これらのオプションのうち、再び最小倀6を遞択し、セル自䜓の゚ネルギヌを远加したす6。 このセルの倀は12です。同様に、3行目の残りの芁玠を怜蚎したす。

この方法ですべおの行を蚈算した埌、結果ずしお次の配列を取埗したす。


この配列の蚈算を圢匏化するず、次の匏が埗られたす。



ここで、sは合蚈の配列、eぱネルギヌの配列です。

合蚈の配列を蚈算する私の実装

 private void FindSum() { //     sum  energy   for (int j = 0; j < imgWidth; j++) sum[0, j] = energy[0, j]; //       (i,j)  sum   // energy[i,j] + MIN ( sum[i-1, j-1], sum[i-1, j], sum[i-1, j+1]) for (int i = 1; i < imgHeight; i++) for (int j = 0; j < imgWidth; j++) { sum[i, j] = sum[i - 1, j]; if (j > 0 && sum[i - 1, j - 1] < sum[i, j]) sum[i, j] = sum[i - 1, j - 1]; if (j < imgWidth - 1 && sum[i - 1, j + 1] < sum[i, j]) sum[i, j] = sum[i - 1, j + 1]; sum[i, j] += energy[i, j]; } } 

この関数は、グロヌバル倉数で機胜したす。energy-゚ネルギヌが曞き蟌たれる配列ずsum-合蚈が曞き蟌たれる配列。
すでにこの配列を持っおいるので、考える時が来たした-なぜそれが必芁なのでしょうか 私は答えたす-この配列から、゚ネルギヌの合蚈が最小のチェヌンをすばやく芋぀けるこずができたす。

たず、画像の䞀番䞋の行のどのピクセルがこのチェヌンに属しおいるかを芋぀けたす。探しおいる芁玠は、䞀番䞋の行の芁玠の䞭でsum配列で最小の倀を持ちたす。 なんで この配列では、䞊端から指定されたピクセルたでの最小チェヌンの芁玠の合蚈の倀が蚘録されるこずを思い出しおください。 興味のあるチェヌンは、最埌の行のピクセルで終了したす。 したがっお、図党䜓に぀いお、最小チェヌンは、最終行からピクセルで終わるすべおの最小チェヌンの最小ずしお遞択されたす。

この䟋では、これは次の芁玠になりたす。


最小チェヌンが終了するピクセルはすでにわかっおいたすが、最埌から2番目の行からピクセルを芋぀けるこずができたす。 すでに説明したように、䞋のピクセルから、䞊の行にある3぀の隣接ピクセル巊䞊、䞊、たたは右䞊にのみ移動できたす。 これらのピクセルの䞭から、合蚈配列の最小倀を持぀ピクセルを遞択しお、そこに移動したす。 トップラむンに達するたで続けたす。 次の図にプロセスを瀺したす。


これらすべおの操䜜を完了するず、必芁なもの、぀たり画像の損倱を最小限に抑えながら倉曎できるピクセルのセットが埗られたす。

図の最小チェヌンの䟋



チェヌン怜玢アルゎリズムの゜フトりェア実装

 private int[] FindShrinkedPixels() { //    int last = imgHeight - 1; //      int[] res = new int[imgHeight]; //     sum,          res[last] res[last] = 0; for (int j = 1; j < imgWidth; j++) if (sum[last, j] < sum[last, res[last]]) res[last] = j; //         . for (int i = last - 1; i >= 0; i--) { // prev -       //         (prev-1), prev  (prev+1),      int prev = res[i + 1]; //   ,     sum,  ,    ,        res[i] res[i] = prev; if (prev > 0 && sum[i, res[i]] > sum[i, prev - 1]) res[i] = prev - 1; if (prev < imgWidth - 1 && sum[i, res[i]] > sum[i, prev + 1]) res[i] = prev + 1; } return res; } 

図面の削枛


最終的に、䜜業に䜿甚するピクセルのチェヌンを芋぀けたした。 今、私たちが圌女を探しおいた理由を思い出すこずができたす。 私たちの仕事は画像のサむズ倉曎です。 画像の拡倧ず瞮小はわずかに異なるため、最初に瞮小を怜蚎し、次に拡倧を怜蚎したす。

画像の幅を1ピクセル枛らす必芁がある堎合、すべおが簡単です。䞊で説明したように、垂盎チェヌンを芋぀けお、画像から削陀するだけです。 次のルヌプでこの操䜜を実装したした。

 for (int i = 0; i < imgHeight; i++) for (int j = cropPixels[i]; j < imgWidth; j++) { pImage[i, j] = pImage[i, j + 1]; } 

cropPixels配列のi番目の芁玠には、ピクセル列の番号が曞き蟌たれ、i番目の行から削陀されたす。 サむクル自䜓は次のこずを行いたす。削陀するピクセルの右偎に曞き蟌たれるすべおのピクセルは、1ポむント巊にシフトしたす。 この操䜜の結果、ピクセルチェヌンは画像から削陀されたす。

むメヌゞを1ピクセルではなく、より倧きな倀で瞮小する必芁がある堎合は、必芁に応じおチェヌンを削陀する操䜜を実行したすこのチェヌンを探す必芁があるたびに。

画像をnピクセル瞮小する操䜜ず、画像を1ピクセルn回瞮小する操䜜は同等ではないこずに泚意しおください。 それらの違いは、1ピクセルず぀n倍に枛らすず、各䞭間画像に぀いおピクセルの゚ネルギヌを探し、すぐにnだけ枛らすず、元の画像からピクセルの゚ネルギヌを取り出すこずです。

図面の拡倧


画像を瞮小する方法はすでにわかっおいたすが、今は画像を拡倧する方法を芋぀ける必芁がありたす。

最初に頭に浮かぶのは、チェヌンを遞択し、チェヌンを遞択しお「ストレッチ」するずきずたったく同じです。 しかし、これを実装するず、次の結果が埗られたす。



ご芧のずおり、プログラムは1列だけを匕き䌞ばしおいたすが、これは必芁なものではありたせん。

たずえば、次のような考えが考えられたす。最小チェヌンを1぀ではなく、必芁な幅に合わせお画像を完成させるのに必芁なだけの量を取る必芁がありたす。 このアルゎリズムを実装したずしたすが、図を2倍にするずどうなりたすか



この図からわかるように、すべおのポむントを遞択しお氎平方向にストレッチしたしたが、この方法は通垞のストレッチず倉わらないため、必芁なものではありたせん。

しかし、原則ずしお、埌者の方法のアむデアそのものは正しいですが、わずかな倉曎がありたす。段階的に画像を拡倧する必芁がありたす。そのため、各段階で画像の「重芁ではない」郚分をできるだけ倚く、「重芁な」郚分をできるだけ少なくしたす。 その埌、増加するプロセスを段階的に分割する方法が問題になりたすか 手動で段階に分割するこずから、ある皮のヒュヌリスティックアナラむザヌを䜜成するこずたで、倚くのオプションがありたす。 しかし、私のプログラムでは、簡単に蚘述したした。段階ぞの分割は、各段階で画像が50を超えお増加しないように行われたす。 これは受け入れられる結果になる堎合もありたすが、そうでない堎合もありたすが、既に曞いたように、ステヌゞぞの分割を実装するための倚くのオプションを考え出すこずができたす。

この方法でUFO画像を拡倧するず、次の結果が埗られたす。



ご芧のずおり、UFO、人、朚は倉化しおいたせん。

䟋


スケヌリング図面の䟋はすべお、この蚘事ず䞊行しお䜜成したプログラムを䜿甚しお取埗し、ここで説明するアルゎリズムを実装しおいたす。

゜ヌスずプログラムバむナリは、蚘事の䞋郚からダりンロヌドできたす。

コンテンツに応じたサむズ倉曎の兞型的な䟋



次の図では、「重芁な」オブゞェクトず背景が発音されおいたす。

オリゞナル 枛少 増加



星空の描画。 サむズを倉曎しおも、星の圢はほずんど倉わりたせん。

オリゞナル 枛少 増加


たた、 gmmで撮圱した2枚の写真をスケヌリングする別の䟋です。

オリゞナル 枛少 増加




オリゞナル 枛少 増加



たずめ



それで、結果ずしお䜕を埗たのか

蚘事を最埌たで読んでくれた人のおかげで、おもしろかったず思いたす。


PS


゜ヌスVS 2008
バむナリ.NET

曎新した
トピックに関する圹立぀リンクリストは補充される堎合がありたす。

Source: https://habr.com/ru/post/J48518/


All Articles