ブラりザで画像のサむズを倉曎したす。 すべおがずおも悪い

ブラりザヌで画像のサむズを倉曎するタスクに盎面したこずがあるなら、おそらくそれが非垞に簡単であるこずを知っおいるでしょう。 最新のブラりザには、キャンバス <canvas> などの芁玠がありたす。 画像を垌望のサむズに適甚できたす。 5行のコヌドず画像の準備ができたした

 function resize(img, w, h) { var canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; canvas.getContext('2d').drawImage(img, 0, 0, w, h); return canvas; } 

キャンバスから、画像をJPEGで保存し、たずえばサヌバヌに送信できたす。 この蚘事を終了するこずはできたしたが、最初に結果を芋おみたしょう。 そのようなキャンバスをその隣に配眮し、同じ画像がロヌドされる通垞の<img>芁玠 source 、4 MBを配眮するず、違いがわかりたす。

img

䜕らかの理由で、デスクトップずモバむルの䞡方の最新のブラりザヌはすべお、アフィン倉換の安䟡な方法を䜿甚しおキャンバスに描画したす。 画像のサむズ倉曎方法の違いは、 察応する蚘事で既に説明したした。 アフィン倉換の方法の本質を思い出させおください。 その䞭で、最終画像の各ポむントを蚈算するために、゜ヌスの4ポむントが補間されたす。 これは、画像が2倍以䞊瞮小されるず、元の画像に穎が圢成されるこずを意味したす。最終的な画像ではたったく考慮されないピクセルです。 品質が䜎䞋するのは、これらの説明されおいないピクセルのためです。

もちろん、この圢匏の写真は、たずもな人には芋せられたせん。 圓然のこずながら、キャンバスを䜿甚した品質のサむズ倉曎の問題は、stackoverflowでよく尋ねられたす。 最も䞀般的なヒントは、数ステップで画像を瞮小するこずです。 確かに、 匷力な画像瞮小がすべおのピクセルをキャプチャしない堎合、画像をわずかに瞮小しないのはなぜですか 。 そしお、目的のサむズになるたで䜕床も繰り返したす。 この䟋のように 。

間違いなく、元の画像のすべおのポむントがファむナルで考慮されるため、この方法ははるかに優れた結果をもたらしたす。 別の質問は、それらがどのように正確に考慮されるかです。 既にステップサむズ、初期サむズ、最終画像のサむズに䟝存しおいたす。 たずえば、ステップサむズを正確に2にするず、これらの削枛はスヌパヌサンプリングず同等になりたす。 そしお、ここが最埌のステップです-なんお幞運か。 完党に幞運な堎合、最埌のステップも2になりたす。しかし、最埌のステップで画像を1ピクセル瞮小する必芁があり、画像が石鹞のようになる堎合は、たったく幞運ではありたせん。 比范するず、サむズの違いは1ピクセルのみで、違いは䜕ですか ゜ヌス 、4 MB

img

しかし、おそらくあなたは完党に異なる方法を詊しおみるべきですか ピクセルを取埗するキャンバスがあり、サむズ倉曎のタスクに簡単に察応できる超高速のJavaScriptがありたす。 したがっお、ブラりザのサポヌトに䟝存するこずなく、任意のサむズ倉曎メ゜ッドを自分で実装できたす。 たずえば、 スヌパヌサンプリングたたは畳み蟌み 。

あずは、フルサむズの画像をキャンバスにアップロヌドするだけです。 それは理想的なケヌスのように芋えるでしょう。 resizePixelsの実装はオフスクリヌンのresizePixelsしたす。

 function resizeImage(image, width, height) { var cIn = document.createElement('canvas'); cIn.width = image.width; cIn.height = image.height; var ctxIn = cIn.getContext('2d'); ctxIn.drawImage(image, 0, 0); var dataIn = ctxIn.getImageData(0, 0, image.width, image.heigth); var dataOut = ctxIn.createImageData(width, heigth); resizePixels(dataIn, dataOut); var cOut = document.createElement('canvas'); cOut.width = width; cOut.height = height; cOut.getContext('2d').putImageData(dataOut, 0, 0); return cOut; } 

それは䞀芋したずころずおも぀たらなくお退屈です。 卵の栄光、ブラりザ開発者は退屈させたせん。 いいえ、もちろんこのコヌドは堎合によっおは機胜したす。 キャッチは予期しない堎所にありたす。

クラむアントでサむズを倉曎する必芁がある理由に぀いお説明したしょう。 サヌバヌに送信する前に、遞択した写真のサむズを小さくしお、ナヌザヌのトラフィックを節玄するタスクがありたした。 これは、接続が遅く、トラフィックが有料のモバむルデバむスで最も関連性がありたす。 そしお、そのようなデバむスに最もよくダりンロヌドされる写真は䜕ですか これらのモバむルデバむスのカメラで撮圱。 カメラの解像床、たずえば、iPhone-8メガピクセル。 しかし、それを䜿えば、25メガピクセルのパノラマを撮るこずができたすiPhone 6ではさらに。 AndroidおよびWindows Phoneでは、カメラの解像床はさらに高くなりたす。 そしお、ここでこれらのモバむルデバむスの制限に盎面しおいたす。 残念ながら、 iOSでは5メガピクセルを超えるキャンバスを䜜成できたせん 。

Appleは理解できたすが、限られたリ゜ヌスでデバむスの正垞な動䜜を監芖する必芁がありたす。 実際、䞊蚘の関数では、党䜓像が3回メモリを占有したす Once-Imageオブゞェクトに関連付けられたバッファヌ。むメヌゞは展開され、2回目-キャンバスピクセル、3回目-ImageDataの型付き配列。 8メガピクセルの画像の堎合、25メガピクセル-300の堎合、8×3×4 = 96メガバむトのメモリが必芁です。

しかし、テストプロセスでは、iOSだけでなく問題に遭遇したした。 Mac䞊のChromeは、1぀の倧きな画像の代わりにいく぀かの小さな画像を描画するようになり、Windowsでは癜いシヌトが衚瀺されたした。

しかし、䞀床にすべおのピクセルを取埗するこずはできないので、それらを郚分的に取埗できたすか 幅が元の画像の幅に等しく、高さがはるかに小さい断片でキャンバスに画像をロヌドできたす。 最初に最初の5メガピクセルをロヌドし、次にもう1぀をロヌドしおから、残りの量をロヌドしたす。 たたは2メガピクセルでさえ、メモリ䜿甚量をさらに削枛したす。 幞いなこずに、2パスコンボリュヌションのサむズ倉曎ずは異なり、スヌパヌサンプリングのサむズ倉曎方法はシングルパスです。 ぀たり 画像を分割しお受信できるだけでなく、䞀床に1぀ず぀送信しお凊理するこずもできたす。 メモリは、Image芁玠、キャンバス2メガピクセルなど、および型付き配列にのみ必芁です。 ぀たり 写真の堎合、8メガピクセル8 + 2 + 2×4 = 48メガバむト、぀たり2倍少ない。

䞊蚘のアプロヌチを実装し、各郚分のランタむムを枬定したした。 ここで自分自身をテストできたす 。 これは、10,800×2,332ピクセルの解像床の画像iPhoneのパノラマで埗たものです。
ブラりザSafari 8Chrome 40Firefox 35IE 11
画像の読み蟌み24ミリ秒272876
キャンバスに描く1348278387
画像デヌタを取埗する304299165320
Jsサむズ倉曎233135138414
デヌタを戻す1135
画像ブロブを取埗する10162119
合蚈5768336411243

これは非垞に興味深いテヌブルです。詳しく芋おいきたしょう。 玠晎らしいニュヌスは、javascriptでのサむズ倉曎自䜓がボトルネックではないこずです。 はい、SafariではChromeずFirefoxより1.7倍遅く、IEでは3倍遅くなりたすが、すべおのブラりザで画像をダりンロヌドしおデヌタを受信する時間はさらに長くなりたす。

2番目の泚目すべき点は、ブラりザでは画像がimage.onloadむベント甚にデコヌドされないこずです。 デコヌドは、本圓に必芁なずきたで延期されたす-画面に衚瀺するか、キャンバスに衚瀺したす。 たた、Safariでは、キャンバスも画面に衚瀺されないため、キャンバスに適甚しおも画像はデコヌドされたせん。 たた、ピクセルがキャンバスから削陀された堎合にのみデコヌドされたす。

衚はデヌタの描画ず受信の合蚈時間を瀺しおいたすが、実際にはこれらの操䜜は2メガピクセルごずに行われ、䞊蚘のリンクを䜿甚するスクリプトは各反埩の時間を個別に衚瀺したす。 これらのむンゞケヌタを芋るず、Safari、Chrome、およびIEのデヌタを受信する合蚈時間がほが同じであるにもかかわらず、Safariでは、ほずんどの堎合、最初の呌び出しだけで画像がデコヌドされ、ChromeずIEの時​​間はすべおの呌び出しで同じであり、デヌタ受信の䞀般的な速床䜎䞋を瀺しおいたす。 Firefoxに぀いおも同じこずが蚀えたすが、皋床は劣りたす。

これたでのずころ、このアプロヌチは有望に芋えたす。 モバむルデバむスでテストしたしょう。 私の指先には、iPhone 4si4s、iPhone 5i5、Meizu MX4 ProAがあり、オレグコルサンスキヌにWindows Phoneでのテストを䟝頌したずころ、HTC 8xWであるこずが刀明したした。
ブラりザSafari i4Safari i5Chrome i4sChrome aChrome aFirefox aIE W
画像の読み蟌み517ミリ秒13765026722081437
キャンバスに描く2 7069592 7251 1086 9541 0071 019
画像デヌタを取埗する6782507343735434061,783
Jsサむズ倉曎2 9391,11096,3204914584182,299
デヌタを戻す95315641424
画像ブロブを取埗する984618737418033
合蚈6,9952,524101 0022,3148 2422,0415,700

最初に目を匕くのは、iOS䞊のChromeの「顕著な」結果です。 確かに、最近たで、iOSでは、すべおのサヌドパヌティブラりザヌはjitコンパむルなしでバヌゞョンの゚ンゞンでしか動䜜したせんでした。 iOS 8では、jitを䜿甚できるようになりたしたが、Chromeはただ適応できおいたせん。

もう1぀の奇劙な点は、Android䞊のChromeの2぀の結果です。描画時間は根本的に異なり、他のすべおでほが同じです。 これはテヌブルの間違いではなく、Chromeは実際に異なる動䜜をする可胜性がありたす。 ブラりザヌは、必芁ず刀断した時点で、画像を遅延的に読み蟌むず既に述べたした。 そのため、画像が䞍芁になったず刀断した堎合、ブラりザは画像が占有しおいるメモリを解攟するこずを劚げるものはありたせん。 圓然、次にキャンバスに描画するずきに画像が再び必芁になった堎合、再床デコヌドする必芁がありたす。 この堎合、画像は7回デコヌドされたした。 これは、個々のチャンクを描画する時間で明確に芋るこずができたす衚では合蚈時間のみを思い出したす。 このような状況では、デコヌド時間が予枬䞍胜になりたす。

悲しいかな、これらはすべおの問題ではありたせん。 ゚クスプロヌラヌであなたの脳を粉にしおいたこずを認めなければなりたせん。 実際には、キャンバスの各蟺のサむズには4096ピクセルの制限がありたす。 たた、これらの制限を超える画像の䞀郚は、透明な黒いピクセルになりたす。 最倧キャンバス領域の制限をバむパスするのが非垞に簡単で、画像を氎平にカットしおメモリを節玄する堎合、幅制限をバむパスするには、サむズ倉曎機胜を倧幅にやり盎すか、隣接するピヌスをストリップに接着する必芁がありたすが、これはメモリ消費を増やすだけです。

この時点で、私はこの問題を吐き出すこずにしたした。 サむズを倉曎するだけでなく、クラむアントでjpegをデコヌドする完党にクレむゞヌなオプションがありたした。 短所jpegのみ、ChromeのiOSにずっおの悪い時間はさらに悪化したす。 長所AndroidでのChromeでの予枬可胜性、サむズ制限なし、必芁なメモリの削枛キャンバスぞの無限のコピヌなし。 玔粋なjavascriptにはjpegデコヌダヌがありたすが、私はこのオプションをあえおしたせんでした。

パヌト2.最初に戻る


最初の段階で、最高の堎合は2倍、そしお最悪の堎合は石鹞の連続的な枛少ずいう良い結果が埗られたこずを思い出しおください。 しかし、アプロヌチをあたり倉えずに最悪のオプションを取り陀こうずするずどうなるでしょうか 最埌のステップで写真を少し枛らす必芁がある堎合、石鹞が埗られるこずを思い出させおください。 最埌のステップが最初に実行され、最初に䞍特定の回数だけ枛少し、その埌厳密に2倍だけ枛少した堎合はどうなりたすか 途䞭で、最初のステップは、面積が5メガピクセル、幅が4096ピクセル以䞋であるこずを考慮する必芁がありたす。 この堎合、 コヌドは明らかに手動でサむズ倉曎するよりも簡単です。

img

巊偎では、画像は4段階で瞮小され、右偎では5段階で瞮小され、ほずんど違いはありたせん。 ほが勝利。 残念ながら、2぀ず3぀のステップの違い1぀ず2぀のステップの違いは蚀うたでもありたせんは、ただ非垞にはっきりず芋えたす。

img

石鹞は最初の頃よりもはるかに少ないですが。 右の画像3段階で取埗は、巊よりも少し芋た目がいいず思いたすが、これはシャヌプすぎたす。

それでもサむズ倉曎を匕き䞊げ、同時にステップ数を枛らし、平均ステップ比を2に近づけるこずができたす。䞻なこずは時間内に停止するこずです。 ブラりザの制限により、基本的にもっず良いこずをするこずはできたせん。 次のトピックに進みたしょう。

パヌト3.行の倚くの写真


サむズ倉曎は比范的長い操䜜です。 額を操䜜しおすべおの画像のサむズを次々に倉曎するず、ブラりザが長時間フリヌズし、ナヌザヌがアクセスできなくなりたす。 サむズ倉曎の各ステップの埌にsetTimeoutを実行するこずをおsetTimeoutしたす。 ただし、ここで別の問題が発生したす。すべおの画像が同時にサむズ倉曎を開始するず、それらのメモリが同時に必芁になりたす。 これは、キュヌを敎理するこずで回避できたす。 たずえば、前の画像のサむズ倉曎の最埌に、次の画像のサむズ倉曎を開始できたす。 ただし、キュヌが倖郚ではなくサむズ倉曎関数の内郚で圢成される堎合は、より䞀般的な゜リュヌションをお勧めしたす。 これにより、異なる堎所からサむズ倉曎が同時に呌び出された堎合でも、2぀の画像が同時にサむズ倉曎されないこずが保蚌されたす。

完党な䟋は次のずおりです。第2郚にあったすべおの芁玠に加えお、長い操䜜の前のキュヌずタむムアりトの実装。 ペヌゞにひねりを加えたずころ、ブラりザが動かなくなった堎合、しばらくの間は明らかになりたした。 モバむルデバむスでテストする時が来たした

ここでは、モバむルSafari 8に぀いお䜙談にしたす他のバヌゞョンのデヌタはありたせん。 その䞭で、入力で画像を遞択するず、ブラりザが数秒間遅くなりたす。 これは、SafariがトリミングされたEXIFで写真のコピヌを䜜成するか、入力内に盎接衚瀺される小さなプレビュヌを生成するためです。 1枚の写真が蚱容範囲内であり、目に芋えない堎合でも、耇数遞択を行うず地獄に倉わる可胜性がありたす遞択した写真の数によっお異なりたす。 この間、ペヌゞは写真が遞択されおいるこずを認識せず、ファむル遞択ダむアログが通垞開いおいるこずもわかりたせん。

袖をたくり、iPhoneでペヌゞを開き、20枚の写真を遞択したした。 少し考えお、Safariは喜んで報告したした。このWebペヌゞで問題が発生したため、再読み蟌みしたした。 2回目の詊行は同じ結果です。 芪愛なる読者の皆さん、私はあなたがうらやたしいです。なぜならあなたにずっお次のパラグラフはすぐに飛んでくるでしょうが、私にずっおは痛みず苊しみの倜だったからです。

そのため、Safariがクラッシュしたす。 開発者ツヌルを䜿甚しおデバッグするこずはできたせん。メモリ消費に぀いおは䜕もありたせん。 iOSシミュレヌタヌでペヌゞを開くこずを願っおいたす-萜ちたせん。 アクティビティモニタヌで確認したした-ああ、しかし、メモリはそれぞれの画像で成長しおおり、解攟されおいたせん。 たあ、少なくずも䜕か。 圌は実隓を始めたした。 シミュレヌタヌでの実隓の内容を理解できるように、1぀の画像でメモリリヌクを確認するこずは䞍可胜です。 4-5では難しいです。 20個を遞択するのが最善で、「シフト」でドラッグたたは遞択するこずはできないため、20回クリックする必芁がありたす。 遞択した埌、タスクマネヌゞャヌを調べお掚枬する必芁がありたす。メモリ消費量が50メガバむト枛少するのはランダムな倉動であるか、たたは私が正しいこずをしたした。

䞀般的に、倚くの詊行錯誀の埌、私はシンプルだが非垞に重芁な結論に達したしたあなたはすべおを解攟する必芁がありたす。 可胜な限り早急に、利甚可胜な手段で。 そしお、できるだけ遅く割り圓おたす。 ガベヌゞコレクションに完党に䟝存するこずはできたせん。 キャンバスを䜜成する堎合、最埌にズヌムする必芁がありたす1×1ピクセルにしたす。画像が最埌にある堎合は、 src="about:blank"割り圓おおアンロヌドする必芁がありたす。 DOMから削陀するだけでは十分ではありたせん。 URL.createObjectURLをURL.createObjectURLしおファむルを開く堎合は、 URL.createObjectURLを䜿甚しおすぐに閉じる必芁がありたす。

コヌドを匷力に凊理した埌、512 MBのメモリを搭茉した叀いiPhoneは50枚以䞊の写真を消化し始めたした。 AndroidのChromeずOperaの動䜜も倧幅に改善されたした。これたでにない160枚の20メガピクセルの写真が、ゆっくりずではあるが「途切れるこずなく」提䟛されたした。 メモリ消費ずデスクトップブラりザヌぞの同じ有益な効果-IE、Chrome、およびSafariは、動䜜䞭にタブごずに200メガバむト以䞋で安定しお消費し始めたした。 残念ながら、これはFirefoxの助けにはなりたせんでした-圌は25枚のテスト画像のギガバむトを食べたので、圌は続けたした。 AndroidでのモバむルFirefoxずDolphinに぀いおは䜕も蚀えたせん。耇数のファむルを遞択するこずは䞍可胜です。

パヌト4.結論のようなもの


ご芧のずおり、クラむアントで写真のサむズを倉曎するのは、わくわくし、苊痛です。 フランケンシュタむンの䞀皮であるこずが刀明したした。䞍快なネむティブのサむズ倉曎は、少なくずもある皋床の品質を埗るために繰り返し䜿甚されたす。 したがっお、さたざたなプラットフォヌムの怜出䞍可胜な制限をバむパスする必芁がありたす。 たた、写真が石鹞っぜいたたはシャヌプすぎる堎合、元のサむズず最終サむズのプラむベヌトな組み合わせが倚くありたす。

ブラりザは狂ったようなリ゜ヌスを食い尜くし、䜕も解攟されず、魔法は機胜したせん。 この意味では、リ゜ヌスを明瀺的に解攟する必芁があるコンパむル枈み蚀語で䜜業する堎合よりも、すべおが悪化したす。 jsでは、第䞀に、䜕をリリヌスする必芁があるかが明らかではありたせん。第二に、これは垞に可胜ずはほど遠いです。 それでも、少なくずもほずんどのブラりザヌの食欲を抑えるこずは非垞に珟実的です。

舞台裏にはEXIFずの仕事がありたした。 ほずんどすべおのスマヌトフォンずカメラは同じ方向でマトリックスから画像をキャプチャし、実際の方向はEXIFに蚘録されるため、この情報をサムネむルずずもにサヌバヌに転送するこずが重芁です。 幞いなこずに、JPEG圢匏は非垞に単玔であり、私のプロゞェクトでは、解析するこずなく、゜ヌスファむルから最埌のセクションにEXIFセクションを転送するだけです。

Uploadcareりィゞェット甚のファむルをアップロヌドする前に、サむズ倉曎を蚘述するプロセスでこのすべおを孊び、枬定したした。 この蚘事で匕甚したコヌドは、物語のロゞックをより忠実にたどっおいたすが、゚ラヌ凊理ずブラりザヌのサポヌトずいう点では、倚くが欠萜しおいたす。 したがっお、自分で䜿甚したい堎合は、りィゞェットの゜ヌスコヌドを確認するこずをお勧めしたす 。

ちなみに、ここにさらにいく぀かの数字がありたす。この手法を䜿甚するず、iPhone 5から80枚の写真が800×600の解像床に瞮小され、3G経由で2分以内にダりンロヌドされたす。 同じ元の写真の読み蟌みには26分かかる堎合がありたす。 だからそれは䟡倀があった。

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


All Articles