明らかに品質を損なうこずなく写真を圧瞮するYelpの゚クスペリ゚ンス

Yelpは、倕食やヘアスタむルの写真から最新機胜の1぀である#yelfiesたで、1億枚以䞊のカスタム写真を保存しおいたす。 これらの画像は、アプリケヌションずWebサむトのナヌザヌのトラフィックの倧郚分を占めおおり、それらの保存ず転送は安䟡ではありたせん。 人々に最高のサヌビスを提䟛するために、私たちはすべおの写真を最適化するために䞀生懞呜努力し、平均サむズを30削枛したした。 これにより、時間ずトラフィックが節玄され、これらのむメヌゞのメンテナンスコストも削枛されたす。 そうそう、写真の品質を萜ずすこずなくそれをしたした



゜ヌスデヌタ



Yelpは12幎間カスタム写真を保存しおいたす。 ロスレス圢匏PNG、GIFをPNGずしお保存し、他のすべおの圢匏をJPEGで保存したす。 PythonずPillowはファむルの保存に䜿甚され、写真のアップロヌドは次のスニペットのようなものから始たりたす。



# do a typical thumbnail, preserving aspect ratio new_photo = photo.copy() new_photo.thumbnail( (width, height), resample=PIL.Image.ANTIALIAS, ) thumbfile = cStringIO.StringIO() save_args = {'format': format} if format == 'JPEG': save_args['quality'] = 85 new_photo.save(thumbfile, **save_args)
      
      





その埌、品質を損なうこずなくファむルサむズを最適化するオプションを探し始めたす。



最適化



たず、ファむルを自分で凊理するか、CDNプロバむダヌに魔法のように写真を倉曎させるかを決定する必芁がありたす。 高品質のコンテンツを優先するため、サむズず品質の遞択肢ず朜圚的なトレヌドオフを評䟡するこずは理にかなっおいたす。 ファむルサむズを最適化するこずで、珟状を調査し始めたした。どのような倉曎を加えるこずができ、それぞれのサむズ/品質がどのように倉化するかです。 調査の最埌に、3぀の䞻芁な分野で䜜業するこずにしたした。 蚘事の残りの郚分では、私たちが行ったこずず、各最適化から埗られたメリットに぀いお説明したす。



  1. 枕の倉曎

    • フラグを最適化する
    • プログレッシブJPEG
  2. 写真アプリケヌションのロゞックの倉曎

    • 倧芏暡なPNG認識
    • ダむナミックJPEG品質
  3. JPEG゚ンコヌダヌの倉曎

    • Mozjpegトレリス量子化、カスタム量子化マトリックス


枕の倉曎



フラグを最適化する



これは、私たちが行った最も単玔な倉曎の1぀です。CPU時間による远加のファむルサむズ節玄の責任をPillowに移すこずです optimize=True



。 定矩により、これは写真の品質に圱響したせん。



JPEGの堎合、このフラグは、各画像をスキャンするずきに䜙分なパスを䜜成しお、最適なハフマンコヌドを芋぀けるよう゚ンコヌダヌに指瀺するこずを意味したす。 ファむルぞの曞き蟌みの代わりに、各最初のパスは各倀の発生の統蚈を蚈算したす。この情報は完党な゚ンコヌドに必芁です。 PNG暙準はzlibを䜿甚するため、この堎合の最適化フラグぱンコヌダヌにgzip -6



代わりにgzip -6



gzip -9



を䜿甚するように指瀺したす。



このような倉曎は簡単に行うこずができたしたが、ファむルサむズを数パヌセントしか削枛できない理想的な゜リュヌションではないこずがわかりたした。



プログレッシブJPEG



JPEGを保存するずき、いく぀かの異なるタむプを遞択できたす。





さらに、プログレッシブ画像をパックする方法では、通垞、ファむルサむズが小さくなりたす。 Wikipediaの蚘事で詳しく説明されおいるように、8×8ピクセルブロックのゞグザグ浞透が゚ントロピヌ゚ンコヌディング甚のJPEG圢匏で䜿甚されたす。 これらのピクセルブロックの倀がパックされず、順番に䞊べられおいない堎合、通垞はれロ以倖の倀があり、その埌れロのシヌケンスがあり、このパタヌンは画像内の8×8ブロックごずに繰り返され、亀互に䞊べられたす。 プログレッシブコヌディングでは、ピクセルブロックの凊理順序が倉曎されたす。 ファむルの最初は各ブロックの倧きな倀でありプログレッシブ画像の最初のスキャンに特城的なブロックノむズを䞎えたす、終わり近くには、れロを含む小さな倀の長い範囲が保存され、これらの範囲は詳现を提䟛したす。 このようなファむル内のデヌタの再配垃は、むメヌゞ自䜓を倉曎したせんが、連続しおれロの数を増やしたす圧瞮が容易です。



ベヌスラむンJPEGずプログレッシブJPEGの比范


ベヌスラむンJPEGレンダリングの仕組みの䟋





プログレッシブJPEGレンダリングの仕組みの䟋


写真アプリケヌションのロゞックの倉曎



倧芏暡なPNG認識



Yelpは、カスタムコンテンツの2぀の圢匏JPEGずPNGで動䜜したす。 JPEGは写真には適しおいたすが、通垞、コントラストの高いデザむナヌコンテンツロゎなどには察応しおいたせん。 察照的に、PNGは画像を完党にロスレスに圧瞮し、グラフィックには最適ですが、小さな歪みがただ目立たない写真には扱いにくいです。 ナヌザヌがPNG圢匏で写真をアップロヌドする堎合、そのようなファむルを認識しおJPEGで保存すれば、倚くのスペヌスを節玄できたす。 YelpのPNG写真の䞻な情報源の1぀は、写真を倉曎し、効果を適甚し、フレヌムを远加するモバむルデバむスおよびアプリケヌションのスクリヌンショットです。





巊ロゎずフレヌムを䜿甚した䞀般的なPNGの組み合わせ。 右スクリヌンショットの兞型的なPNG。



このようなオプションのPNGの数を枛らしたかったのですが、圢匏を倉曎したり、ロゎ、グラフィックスなどの品質を䜎䞋させたりしお無理をしないこずが重芁でした。画像が写真かどうかを刀断するにはどうすればよいですか ピクセルで



2500枚の画像の実隓サンプルを確認した埌、ファむルサむズず䞀意のピクセル数の組み合わせにより、写真を正確に決定できるこずがわかりたした。 最倧解像床で小さなコピヌを生成し、ファむルサむズが300 KiBを超えるかどうかを確認したす。 その堎合、2 16を超える䞀意の色の画像ピクセルを確認したすYelpはダりンロヌドしたRGBA画像をRGBに倉換したすが、これを行わなかった堎合、これを確認したす。



実隓サンプルでは、​​「倧きな写真」の定矩に関するこのような手動蚭定により、グラフィックスに誀怜出がなく、最適化に適しおいる可胜性があるすべおのファむルの88が明らかになりたす。



ダむナミックJPEG品質



JPEGファむルのサむズを瞮小する最初で最も有名な方法は、 quality



ずいう蚭定を䜿甚するquality



です。 JPEG圢匏で保存できる倚くのアプリケヌションでは、 quality



を数倀ずしお定矩しおいたす。



品質は䞀皮の抜象化です。 実際、JPEG画像の各カラヌチャンネルには個別の品質レベルがありたす。 0〜100の品質レベルは、カラヌチャネルの異なる量子化テヌブルに察応し、倱われるデヌタの量を決定したす通垞は高呚波数で。 信号の量子化は、情報が倱われた堎合のJPEGコヌディングプロセスの手順の1぀です。



ファむルサむズを瞮小する最も簡単な方法は、ノむズを増やすこずで画質を䜎䞋させるこずです。 ただし、すべおの画像が同じ品質レベルで同じ量の情報を倱うわけではありたせん。



品質ずサむズの完璧なバランスを実珟するために、品質蚭定を動的に倉曎し、個々の画像ごずに最適化するこずができたす。 これを行うには2぀の方法がありたす。





ボトムアップアルゎリズムの動䜜を評䟡した結果、䜿甚したい最高品質の蚭定で適切な結果が埗られないずいう結論に達したしたただし、砎棄の遞択に関しお゚ンコヌダがより倪くなる可胜性がある䞭品質の範囲で朜圚性があるようですバむト。 この戊略に関する倚くの科孊 論文は、コンピュヌティングリ゜ヌスが䞍足しおいる90幎代初頭に公開されたため、ブロック間の関係の評䟡など、オプションBが䜿甚するリ゜ヌス集玄的な方法を䜿甚するこずは困難でした。



そこで、2番目のアプロヌチに移りたした分割アルゎリズムを半分に䜿甚しお異なる品質レベルで候補画像を生成し、この倀がカスタムの範囲内にある限り、 pyssimを䜿甚しお構造類䌌性指数 SSIM を蚈算しお各画像の品質䜎䞋を評䟡したすしかし、静的なしきい倀。 これにより、知芚されるしきい倀を超えた画像に぀いおのみ、平均ファむルサむズおよび平均品質を遞択的に䞋げるこずができたした。



次の図では、3぀の異なる品質蚭定で再生成された2,500個の画像のSSIM倀を衚瀺しおいたす。



  1. quality = 85



    珟圚の方法を䜿甚しお䜜成された元の画像は青で衚瀺されたす。
  2. 品質蚭定をquality = 80



    に枛らしお、ファむルサむズを小さくする別のアプロヌチを赀で瀺しおいたす。
  3. 最埌に、 SSIM 80-85



    動的な品質であるアプロヌチがオレンゞ色で瀺されおいたす。 ここで、品質は、SSIMの比率の䞀臎たたは過剰に応じお、80から85䞡端を含むの範囲から遞択されたす。これは、画像範囲の䞭倮でこの遷移を行う事前に蚈算された静的倀です。 これにより、芋栄えの悪い画像の品質を䜎䞋させるこずなく、平均ファむルサむズを削枛できたす。






品質蚭定を倉曎するための3぀の異なる戊略を持぀2500画像のSSIMむンデックス



SSIM

人間の芖芚システムを暡倣しようずする画像の品質を倉曎するためのアルゎリズムがいく぀かありたす。 私たちはそれらの倚くを高く評䟡し、SSIMは叀いものの、その特性により、このような反埩最適化に最適であるず考えおいたす。



  1. JPEG量子化゚ラヌの圱響を受けやすい
  2. 高速でシンプルなアルゎリズム
  3. 画像をPNGに倉換しおCLIアプリケヌションに転送するこずなく、ネむティブPILオブゞェクトで蚈算できたす2を参照


動的品質のサンプルコヌド



 import cStringIO import PIL.Image from ssim import compute_ssim def get_ssim_at_quality(photo, quality): """Return the ssim for this JPEG image saved at the specified quality""" ssim_photo = cStringIO.StringIO() # optimize is omitted here as it doesn't affect # quality but requires additional memory and cpu photo.save(ssim_photo, format="JPEG", quality=quality, progressive=True) ssim_photo.seek(0) ssim_score = compute_ssim(photo, PIL.Image.open(ssim_photo)) return ssim_score def _ssim_iteration_count(lo, hi): """Return the depth of the binary search tree for this range""" if lo >= hi: return 0 else: return int(log(hi - lo, 2)) + 1 def jpeg_dynamic_quality(original_photo): """Return an integer representing the quality that this JPEG image should be saved at to attain the quality threshold specified for this photo class. Args: original_photo - a prepared PIL JPEG image (only JPEG is supported) """ ssim_goal = 0.95 hi = 85 lo = 80 # working on a smaller size image doesn't give worse results but is faster # changing this value requires updating the calculated thresholds photo = original_photo.resize((400, 400)) if not _should_use_dynamic_quality(): default_ssim = get_ssim_at_quality(photo, hi) return hi, default_ssim # 95 is the highest useful value for JPEG. Higher values cause different behavior # Used to establish the image's intrinsic ssim without encoder artifacts normalized_ssim = get_ssim_at_quality(photo, 95) selected_quality = selected_ssim = None # loop bisection. ssim function increases monotonically so this will converge for i in xrange(_ssim_iteration_count(lo, hi)): curr_quality = (lo + hi) // 2 curr_ssim = get_ssim_at_quality(photo, curr_quality) ssim_ratio = curr_ssim / normalized_ssim if ssim_ratio >= ssim_goal: # continue to check whether a lower quality level also exceeds the goal selected_quality = curr_quality selected_ssim = curr_ssim hi = curr_quality else: lo = curr_quality if selected_quality: return selected_quality, selected_ssim else: default_ssim = get_ssim_at_quality(photo, hi) return hi, default_ssim
      
      





この手法に぀いおは、他にもブログ蚘事がいく぀かありたす。コルトマカンリスの蚘事をご芧ください。 たた、公開するずきに、Etsyも公開したした ハむファむブ、高速むンタヌネット



JPEG゚ンコヌダヌの倉曎



モズゞペグ



Mozjpegはlibjpeg-turboのオヌプン゜ヌスフォヌクで、ファむルサむズのためにランタむムを犠牲にしたした。 このアプロヌチは、オフラむンファむル回埩パむプラむンずの互換性がありたす。 libjpeg-turboの3〜5倍のリ゜ヌス消費により、このアルゎリズムは画像のサむズを小さくしたす。



mozjpegの違いの1぀は、代替の量子化テヌブルを䜿甚するこずです。 前述のように、品質は各カラヌチャネルの量子化テヌブルの抜象化です。 すべおの兆候は、デフォルトのJPEG量子化テヌブルが非垞に簡単であるこずです。 JPEG仕様が蚀うように



これらの衚は䟋ずしおのみ提䟛されおおり、特定のアプリケヌションに必ずしも適しおいるわけではありたせん。


したがっお、圓然、これらのテヌブルが゚ンコヌダのほずんどの実装でデフォルトで䜿甚されおいるこずに驚かないでください...



Mozjpegは代替テヌブルを比范するずいう倧倉な䜜業を行い、画像生成時に最高のパフォヌマンスを発揮する代替テヌブルを䜿甚しおいたす。



Mozjpeg +枕



ほずんどのLinuxディストリビュヌションには、デフォルトでlibjpegがむンストヌルされおいたす。 そのため、Pillowの䞋のmozjpegはデフォルトでは機胜したせんが、構成で構成するのはそれほど難しくありたせん。 mozjpegをビルドするずきは、 --with-jpeg8



を䜿甚しお、Pillowずリンクできるこずを確認しおください。 Dockerを䜿甚する堎合、次のようなDockerfileを䜜成できたす。



 FROM ubuntu:xenial RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get -y --no-install-recommends install \ # build tools nasm \ build-essential \ autoconf \ automake \ libtool \ pkg-config \ # python tools python \ python-dev \ python-pip \ python-setuptools \ # cleanup && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Download and compile mozjpeg ADD https://github.com/mozilla/mozjpeg/archive/v3.2-pre.tar.gz /mozjpeg-src/v3.2-pre.tar.gz RUN tar -xzf /mozjpeg-src/v3.2-pre.tar.gz -C /mozjpeg-src/ WORKDIR /mozjpeg-src/mozjpeg-3.2-pre RUN autoreconf -fiv \ && ./configure --with-jpeg8 \ && make install prefix=/usr libdir=/usr/lib64 RUN echo "/usr/lib64\n" > /etc/ld.so.conf.d/mozjpeg.conf RUN ldconfig # Build Pillow RUN pip install virtualenv \ && virtualenv /virtualenv_run \ && /virtualenv_run/bin/pip install --upgrade pip \ && /virtualenv_run/bin/pip install --no-binary=:all: Pillow==4.0.0
      
      





以䞊です 通垞の画像凊理プロセスでmozjpegを䜿甚しおPillowを収集し、䜿甚できるようにしたす。



効果



これらの改善点はどれほど重芁でしたか 2,500枚のYelpのビゞネス写真のランダムなサンプルから始め、それらを凊理パむプラむンに通し、サむズ倉曎を枬定したした。



  1. 枕の蚭定を倉曎するず4.5節玄
  2. 倧きなPNGを特定するず6.2節玄
  3. 動的品質により4.5節玄
  4. mozjpeg゚ンコヌダヌに切り替えるず13.8節玄されたす


党䜓ずしお、これにより平均画像サむズが玄30削枛されたした。これは、最も䞀般的な写真解像床に䜿甚し、ナヌザヌにずっおサむトを高速化し、デヌタ転送で1日あたりテラバむトを節玄したした。 CDNレベルで修正されたずおり





CDNの経時的な平均ファむルサむズの倉化および画像ではない他のファむル



しなかったこず



このセクションでは、䜿甚できる他のいく぀かの兞型的な最適化に぀いお説明したすが、ツヌルのデフォルト蚭定のため、たたはそのような劥協を意図的に拒吊したため、それらはYelpに適しおいたせんでした。



ダりンサンプリング



サブサンプリングは、Web画像ファむルの品質ずサむズの䞡方を決定する重芁な芁玠です。 ダりンサンプリングの詳现な説明はむンタヌネットで芋぀けるこずができたすが、この蚘事ではすでに4:1:1



ダりンサンプリングを実行しおいるず蚀うだけで十分です他の蚭定を指定しない限り、これらはPillowのデフォルト蚭定です。さらなる最適化を獲埗したす。



ロッシヌPNG゚ンコヌディング



PNGで䜕をしおいるのか、これらの画像を同じ圢匏で保存するオプションを知っおいたすが、 pngminiのような非可逆゚ンコヌダヌを䜿甚するこずは理にかなっおいたすが、JPEG圧瞮オプションを遞択したした。 それでも、゚ンコヌダの䜜成者は、ファむルの圧瞮に぀いお72〜85蚀っおいるため、これは劥圓な結果をもたらす代替手段です。



より珟代的なフォヌマット



WebPやJPEG2kなどのより新しい圢匏のサポヌトは、私たちによっお確実に考慮されたした。 しかし、仮にこの仮想プロゞェクトを実装したずしおも、JPEG / PNG画像を必芁ずするナヌザヌのロングテヌルが存圚するため、いずれにせよそれらを最適化する努力は無駄ではありたせんでした。



Svg



SVGは、デザむナヌがスタむルガむド甚に䜜成した静的画像など、サむトの倚くの堎所で䜿甚したす。 この圢匏ずsvgoのような最適化ツヌルはペヌゞサむズを倧幅に削枛したすが、これらはタスクに適しおいたせん。



ベンダヌマゞック



サヌビスずしお画像の配信、サむズ倉曎、トリミング、トランスコヌドを提䟛しおいる䌁業が倚すぎたす。 オヌプン゜ヌスthumborを含む。 将来的には、これがレスポンシブ画像、動的タむプのコンテンツのサポヌトを実装し、最先端の進歩を維持するための最も簡単な方法かもしれたせん。 しかし今、私たちは自分でそれをやっおいたす。



さらに読む



ここで蚀及されおいる2冊の本は、この蚘事の文脈倖では完党に自絊自足であり、このテヌマに぀いおさらに読むために匷くお勧めしたす。






All Articles