C++ ときどき ごはん、わりとてぃーぶれいく☆

Wonder Rabbit Projectのなかのひとのブログ。主にC++。

WPF,XAML: Image に Source で入れると極端にぼやけたり何かがおかしい JPEG に遭遇してしまった時の簡単な対処方法

仕事で扱ってもいるので国土地理院地理院地図のタイル画像データはよく使わせて頂いています。います、のですが、これがたまにメタデータだったりピクセルフォーマットだったりが奇妙な画像ファイルに遭遇する事がたびたびあります😅

今回は地理院地図のJPEGファイルの一部を WPF の Image コントロールで読ませると極端にぼやけてしまうという怪奇現象に遭遇しました。

https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/17/117000/48132.jpg

↑一見ふつーのJPEGファイルのようにウェブブラウザーやほとんどの画像ビューアー等では表示できるのですが…

<!-- 何かがおかしい JPEG をソースに入れると何かがおかしいレンダリング結果が得られてしまう -->
<Image Height="60" Width="256" RenderOptions.BitmapScalingMode="Fant" Stretch="None" Source="https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/17/117000/48132.jpg"/>

↑これが実行すると↓こんな表示になってしまう😂

f:id:USAGI-WRP:20181221233253p:plain

↑この JPEG ファイルのバイナリー↓からメタデータを解析すると

f:id:USAGI-WRP:20181221234652p:plain

  • 0x0D ( 1 byte ) = 0x01 ( "Density units", 0x01 = "Pixels per inch" )
  • 0x0E ( 2 bytes; littile-endian ) = 0x0001 ( "Xdensity"; Horizontal pixel density )
  • 0x10 ( 2 bytes; littile-endian ) = 0x0001 ( "Ydensity"; Horizontal pixel density )

と、なっていました。「ピクセルの密度単位が 1 pixels/in 」という事は、「1 ピクセルにつき 1 in = 2.54 cm に展開」という事なので、地理院地図のタイル 256x256 pixels は「 6.5 m x 6.5m の表示サイズに展開されるのが正しい」というメタデータになっているのです。なるほど真面目にメタデータを読んじゃったら極端にぼやけるよね(´・ω・`)

<!-- とりあえず簡単な対処方法 -->
<Image Height="60" Width="256" RenderOptions.BitmapScalingMode="Fant" Stretch="UniformToFill" Source="https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/17/117000/48132.jpg"/>

ドットバイドットを期待して StretchNone にしたくなるお気持ちを抑えて UniformToFill を使います。(あるいは ImageWidthHeight の縦横比が画像と同じなら Fill でも期待する表示にできるでしょう😃)

f:id:USAGI-WRP:20181221231804p:plain

XAMLの属性だけで簡単にどうにかできました😋

XAMLではなくソースコード書くぞー的に対応したい場合は、ソース取得時のストリームに介入するか、デコーダーに介入するか、 BitmapImage でデコード後の画像データの分解能情報を妥当な値へ修正するか、ピクセル配列をさらって適当なリソース型へ複製しちゃうなどお好みのレベルで対応できると思います。が、今回は XAML でプロパティー値をちょっと気にするだけで対応できたのでそういうはなしは無しで。

ちなみに、一般的にはパソコンやスマフォなどの一般的なディスプレイ向けのピクセル密度、画像分解能は 96 pixels/in ( DPI=dots/in でもおなじ )に設定されています(特に Windows では長いこと 96 DPI 基準が染み付いています)。 そうしなければならない、という決まりがあるわけではない慣習による値ですが、ヘッダーをてきとーに作ってしまうと今回のように画像ファイルを使用するユーザー側で思わぬトラブルや扱いに手間暇の必要な事態になってしまう事がたまによく起こります😂

96 DPI = 0.01041666666 in/pixel = 0.02645833333 cm/pixel での 256 pixels は 6.77333333333 cm となります。まあ、そんなもんかな、という大きさの感覚で見られるように思います。少なくとも縦横 6.5m はいくらなんでもデータ生成の誤りでしょう。

のちほど、本件は適当な方法で地理院タイルのなかのひとに伝わるように報告しておこうと思います。以前、規則性不明にタイルの一部のPNGファイルが16-Bitピクセルフォーマットで提供されているためにデコーダー、ライブラリーによっては対応できない事がある問題を地理院パートナーネットワークのアンケートで報告した際には数カ月後にしれっと対応して頂けていたし、 HTTP/2 対応なども要望したらいつの間にかしれっと対応、タイルデータの配布も高速にできるよう何かと工夫を進めてくれているので、本件も適当に報告しておけばいつの間にかどうにかしてくれるのではないかな、と期待しています😋

References