Android の Adaptive Icon を設定する際、元のアイコンが比較的複雑なアイコン (例えば、ゲームのアイコン) だと、そのままだとアイコンをミニマルテーマにしたときに、意図しない表示になってしまう、ということがありました。

Android 公式サイトとか見ても、シンプルなアイコンを白黒でぶちこめ!みたいなことしかかいて無くて、ブルアカはどうやってキレイにアイコンにしているんだろうと思って海外サイトを探していたら、解決方法を見つけたので紹介します。
方法は簡単で、グレースケールの濃さの要素を画像の Alpha 値として使うだけです。 画像の変換には次のスクリプトを使いました:
#!/usr/bin/env python3 """ Android Adaptive Icon モノクロ変換スクリプト グレースケールの輝度を Alpha 値にマッピングすることで、 Android Adaptive Icon の monochrome スタイルに対応した画像を生成します。 変換ルール (デフォルト: 暗さ → Alpha): 暗いピクセル → Alpha 高 (不透明) 明るいピクセル → Alpha 低 (透明) 変換ルール (--invert: 明るさ → Alpha): 明るいピクセル → Alpha 高 (不透明) 暗いピクセル → Alpha 低 (透明) 使い方: python scripts/to-monochrome-alpha.py <input> <output> [options] 例: python scripts/to-monochrome-alpha.py assets/icon.png assets/icon-monochrome.png python scripts/to-monochrome-alpha.py assets/icon.png out.png --color 255 255 255 python scripts/to-monochrome-alpha.py assets/icon.png out.png --gamma 1.5 python scripts/to-monochrome-alpha.py assets/icon.png out.png --invert """ import argparse import sys try: from PIL import Image except ImportError: print("Error: Pillow が見つかりません。`pip install Pillow` でインストールしてください。", file=sys.stderr) sys.exit(1) try: import numpy as np HAS_NUMPY = True except ImportError: HAS_NUMPY = False def convert_numpy(img: "Image.Image", color: tuple[int, int, int], gamma: float, invert: bool) -> "Image.Image": rgba = np.array(img.convert("RGBA"), dtype=np.float32) # 輝度 (Rec.709) を計算 luminance = ( rgba[:, :, 0] * 0.2126 + rgba[:, :, 1] * 0.7152 + rgba[:, :, 2] * 0.0722 ) # invert=False: 暗さ = 1 - 輝度 / invert=True: 明るさ = 輝度 weight = luminance / 255.0 if invert else 1.0 - (luminance / 255.0) # ガンマ補正でコントラストを調整 if gamma != 1.0: weight = np.power(np.clip(weight, 0.0, 1.0), 1.0 / gamma) # 元の Alpha と合成 orig_alpha = rgba[:, :, 3] / 255.0 final_alpha = np.clip(weight * orig_alpha * 255.0, 0, 255).astype(np.uint8) # 出力画像を組み立て result = np.zeros((*img.size[::-1], 4), dtype=np.uint8) result[:, :, 0] = color[0] result[:, :, 1] = color[1] result[:, :, 2] = color[2] result[:, :, 3] = final_alpha return Image.fromarray(result, "RGBA") def convert_pure_pil(img: "Image.Image", color: tuple[int, int, int], gamma: float, invert: bool) -> "Image.Image": rgba = img.convert("RGBA") grayscale = img.convert("L") result = Image.new("RGBA", img.size) rgba_pixels = rgba.load() gray_pixels = grayscale.load() result_pixels = result.load() width, height = img.size for y in range(height): for x in range(width): r, g, b, a = rgba_pixels[x, y] gray = gray_pixels[x, y] # invert=False: 暗さ / invert=True: 明るさ weight = gray if invert else 255 - gray # ガンマ補正 if gamma != 1.0: weight = int(((weight / 255.0) ** (1.0 / gamma)) * 255) # 元 Alpha と合成 final_alpha = int(weight * a / 255) result_pixels[x, y] = (*color, final_alpha) return result def main() -> None: parser = argparse.ArgumentParser( description="グレースケールの暗さを Alpha 値にマッピングして Android Adaptive Icon 用モノクロ画像を生成する", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__doc__, ) parser.add_argument("input", help="入力画像パス (PNG 推奨)") parser.add_argument("output", help="出力画像パス (.png)") parser.add_argument( "--color", nargs=3, type=int, default=[255, 255, 255], metavar=("R", "G", "B"), help="前景色 RGB (デフォルト: 255 255 255 = 白)", ) parser.add_argument( "--gamma", type=float, default=1.0, metavar="GAMMA", help="ガンマ値でコントラストを調整 (>1.0 で暗部を強調, デフォルト: 1.0)", ) parser.add_argument( "--invert", action="store_true", help="明るいピクセルを Alpha 高 (不透明) にする (デフォルトは暗いピクセルが不透明)", ) args = parser.parse_args() color = tuple(args.color) for c in color: if not (0 <= c <= 255): parser.error(f"--color の値は 0〜255 の範囲で指定してください: {c}") if args.gamma <= 0: parser.error("--gamma は正の値を指定してください") try: img = Image.open(args.input) except FileNotFoundError: print(f"Error: ファイルが見つかりません: {args.input}", file=sys.stderr) sys.exit(1) except Exception as e: print(f"Error: 画像を開けませんでした: {e}", file=sys.stderr) sys.exit(1) print(f"入力: {args.input} ({img.size[0]}x{img.size[1]})") print(f"前景色: rgb{color}") print(f"ガンマ: {args.gamma}") print(f"モード: {'明るさ → Alpha (--invert)' if args.invert else '暗さ → Alpha'}") print(f"エンジン: {'numpy' if HAS_NUMPY else 'pure Pillow (numpy なし)'}") if HAS_NUMPY: result = convert_numpy(img, color, args.gamma, args.invert) else: result = convert_pure_pil(img, color, args.gamma, args.invert) if not args.output.lower().endswith(".png"): print("Warning: 出力ファイルは PNG 形式を推奨します (Alpha チャンネルを保持するため)", file=sys.stderr) result.save(args.output) print(f"出力: {args.output}") if __name__ == "__main__": main()
これを元に、
$ python3 ./scripts/to-monochrome-alpha.py 元画像 出力先パス --gamma 1.5 # もしくは $ python3 ./scripts/to-monochrome-alpha.py 元画像 出力先パス --gamma 1.5 --invert
として、画像の濃さを Alpha 値として書き出せば、完了です。 あとはこれを monochrome 画像として設定することで、この通り:

満足のいくアイコンとなりました。ということでめでたしめでたし。
参考: