透明なのに「中身あり」判定されるPNGの罠。余白が消えなかった原因と対処法

透明なのに「中身あり」判定されるPNGの罠

きっかけは小さな違和感でした。自己紹介動画用にロゴ画像を切り抜いていたときの話です。「余白が思ったより大きいな」という、それだけ聞くとどうでもよさそうな違和感から、地味に手強いバグに突き当たりました。前回のnvm/npmの件といい、今回の動画づくり、本編にたどり着くまでのハマりポイントが多すぎる気がしています。

🎥 この記事の内容は動画でも解説しています。

切り抜いたはずなのに、なぜか余白がでかい

作業自体は単純でした。ロゴのPNGから肉球アイコンとワードマーク(「Cat’s Hand」の文字部分)をそれぞれ切り抜いて、動画に組み込んでいました。Pythonの画像処理ライブラリPillowで、透明部分を除いた実際の中身の範囲(バウンディングボックス)だけをトリミングする、よくあるやり方です。

予想は外れました。ワードマークの方を切り抜いたとき、見た目には特に違和感なかったんですが、動画にレンダリングしてみると、文字とロゴアイコンの間の余白が明らかに広すぎました。CSS側のmarginを疑って何度か数値をいじってみたものの、一向に思った通りにならず、「あれ、これ数値の問題じゃないな」と気づくまでに、地味に時間を溶かしました。

疑う先を間違えていました。最初のうちは「CSSのtransform: scale()の影響で余白が拡大されて見えてるだけかも」とか「flexboxのgapがどこかで二重に効いてるのかも」とか、コード側の可能性を一通り潰していきました。一つずつ検証しては「これも違う」の繰り返しで、正直だんだん心が折れかけていました。

画像そのものを疑ってみた

見た目は正常でした。試しに切り抜いた後のPNGファイル単体を画像ビューアで開いてみたら、一見普通に見えました。文字はちゃんと写っているし、余計なゴミも見当たりません。でも念のため、画像のピクセルデータを直接調べてみることにしました。

答えはここにありました。Pillowのgetbbox()という関数で「中身がある範囲」を取得しているんですが、これが返してきた座標をよくよく見ると、実際に文字が写っている範囲よりずっと広い範囲を「中身あり」と判定していました。透明なはずの領域に、目に見えないレベルのごく薄いアルファ値(不透明度)が残っていて、それをgetbbox()が律儀に拾っていたんです。

「見た目は透明」と「データ上も透明」は別物だった

盲点はここでした。これ、言われてみれば当たり前なんですが、実際にハマるまで意識したことがありませんでした。人間の目には完全に透明に見えても、アルファ値が0ではなく1とか2とかになっているピクセルは普通に存在し得ます。今回の場合、元のPNGを切り抜く過程でアンチエイリアス(輪郭のぼかし処理)がうっすら滲んでいたのが原因でした。

百聞は一見に如かず。視覚的な確認だけでは絶対に気づけないタイプのバグで、実際に赤い枠線を描画してバウンディングボックスを可視化してみて、ようやく「ここまで拾ってるのか」と目で見て納得できました。数値やコードを読んでいるだけでは信じきれず、実際に描いて確かめて初めて腹落ちする、というのは今回に限らずよくあるパターンだなと思います。

想像以上の広さでした。可視化してみて分かったのは、透明領域が拾われていた範囲が、想像していたよりずっと広かったことです。ほんの数ピクセルのズレだろうと高をくくっていたら、実際には画像の高さの半分近くを占めるくらいの「見えない中身」が判定されていて、「そりゃ余白もでかくなるわ」と一人で納得してしまいました。

直し方はシンプルだった

対応はあっけないものでした。原因さえ分かれば対処は簡単で、getbbox()をそのまま使うのをやめて、アルファ値に閾値(今回は30くらい)を設けて、それを超えるピクセルだけを「中身」とみなす形に変えました。NumPyで配列化してマスクを作って、その範囲だけを抜き出す、という数行の処理です。これで無駄な余白がすっぱり消えて、想定通りの見た目になりました。

「見えないから存在しない」とは限らない

教訓はシンプルです。今回の件で思ったのは、これって画像処理に限った話じゃないよなということです。コードを書いていても、「エラーが出ていないから大丈夫」「画面上おかしく見えないから問題ない」と判断してしまう場面、割とよくあります。でも実際には、見た目や表面上の挙動だけでは判断しきれない「データ上の事実」が裏に潜んでいることがある。今回はそれがたまたま画像のアルファ値だっただけで、似たような構造の落とし穴は他の場面にもいくらでもありそうだなと、ちょっと背筋が伸びる思いでした。

まとめ

結論はこれです。PNGの自動トリミングで「なぜか余白が広い」となったら、CSSのmarginやpaddingを疑う前に、画像データ自体のアルファ値を疑ってみるのも手だと思います。目に見えない透明ピクセルのノイズが、見えない形で結果を歪めることがある、というのは今回身をもって学びました。地味な話ですが、同じところで詰まっている人の助けになれば嬉しいです。

この記事の内容を、YouTube Shortsでも紹介しています

この記事を書いた人

Web制作歴10年近く。WordPressのテーマカスタマイズやコーディングを中心に、副業・本業ともにweb制作に携わっています。このブログでは、実際に手を動かして分かったことを書いています。

Cat's Handをフォローする

この記事が参考になったら、応援クリックをお願いします

タイトルとURLをコピーしました