プログラマ・アゲイン blog

還暦を過ぎたけどプログラマ復帰を目指してブログ始めました

iPhone/iPadのアプリ・アイコンを作成するPythonプログラム

iPhoneのアプリを作成してみたいと思い立ち、Swift/SwiftUIを勉強中です。

その為、今までWindows OSしか使った事が無かったのに、ついにMac OSを使いだしました。

いろいろ、ビックリすることがたくさんあります。

その中の一つに、iPhoneアプリのアイコンにはいろいろなサイズを事前に作っておくということがあります。

てっきり 1つのアイコンを作成すれば、iPhoneの画面サイズに合わせて自動的にリサイズしてくれるもんだと思ってました。

そこで、元ネタのアイコン画像から、iPhoneiPadの各種サイズのアプリ・アイコン画像ファイルと、その画像ファイル情報の jsonファイルも作成する、Pythonプログラムを作りました。

ここでは、作成した Pythonプログラムを記録したいと思います。

 

 

概要

作成するアイコン画

作成する画像ファイルの種類については、以下のページで作成したものとほぼ同じようにしました。

makeappicon.com

上記サイトの使い方については、以下の本に載ってます。

 

上記のサイトで画像を作成すると、zipファイルにしてメールで送ってきてくれます。

それを解凍すると、(2022年7月では)以下のようなファイルが含まれています。

appiconフォルダー (←appicon.zip)
    androidフォルダ―
    imessengerフォルダ―
    iosフォルダ―
        AppIcon.appiconsetフォルダー
            Contents.json
            Icon-App-20x20@1x.png
            Icon-App-20x20@2x.png
            Icon-App-20x20@3x.png
            Icon-App-29x29@1x.png
            Icon-App-29x29@2x.png
            Icon-App-29x29@3x.png
            Icon-App-40x40@1x.png
            Icon-App-40x40@2x.png
            Icon-App-40x40@3x.png
            Icon-App-60x60@2x.png
            Icon-App-60x60@3x.png
            Icon-App-76x76@1x.png
            Icon-App-76x76@2x.png
            Icon-App-83.5x83.5@2x.png
            ItunesArtwork@2x.png
        iTunesArtwork@1x.png
        iTunesArtwork@2x.png
        iTunesArtwork@3x.png
        README.md
    watchkitフォルダ―

 

今回作成するのは、ios → AppIcon.appiconset 配下の Contents.json と Icon-App-・・・.png の合計 15ファイルです。

 

Pythonの環境

使用したPythonとライブラリーのバージョンは、以下です。

Python 3.10.5
Package            Version
------------------ -----------
opencv-python      4.6.0.66

 

Pythonの参考書

Pythonプログラムをコーディングするにあたって、以下の本で勉強しました。

最後の章の中に画像処理が載っているので、参考にしています。

 

プログラム処理

Pythonのソース

Pythonのソースの全体は、以下のようになりました。

import cv2
import json

# get original image file
original = 'data/idm_icon.png'
img = cv2.imread(original)
height = img.shape[0]
width = img.shape[1]
print('original file =', original, ',height =', height, ',width =', width)

# resize pattern and contents.json images
pattern = {20:(1,2,3),29:(1,2,3),40:(1,2,3),60:(2,3),76:(1,2),83.5:(2,0)}
jsonimg = {'iphone':((20,2),(20,3),(29,1),(29,2),(29,3),(40,2),(40,3),(60,2),(60,3),(76,2)),
           'ipad':((20,1),(20,2),(29,1),(29,2),(40,1),(40,2),(76,1),(76,2),(83.5,2))}

# resize image to several size
images = []
for size in pattern.keys():
    list = pattern[size]
    for scale in list:
        if scale > 0:
            resized_img = cv2.resize(img, (int(size*scale), int(size*scale)))
            # write image to file
            imgfile = 'data/appicon/Icon-App-' + str(size) + 'x' + str(size) + '@' + str(scale) + 'x.png'
            cv2.imwrite(imgfile, resized_img)

# create contents.json file
jsonfile = 'data/appicon/Contents.json'
json_dict = {}
img_list = []
for device in jsonimg.keys():
    for ss in jsonimg[device]:
        img_dict = {}
        img_dict['idiom'] = device
        img_dict['size'] = str(ss[0]) + 'x' + str(ss[0])
        img_dict['scale'] = str(ss[1]) + 'x'
        img_dict['filename'] = 'Icon-App-' + str(ss[0]) + 'x' + str(ss[0]) + '@' + str(ss[1]) + 'x.png'
        img_list.append(img_dict)
json_dict['images'] = img_list
info_dict = {}
info_dict['version'] = 1
info_dict['author'] = 'ALNsystem'
json_dict['info'] = info_dict
json_file = open(jsonfile, 'w')
json.dump(json_dict, json_file, indent=4)
json_file.close()

次に各処理について、メモします。

 

ライブラリー読み込み

まず最初に、必要となるライブラリーを読み込みます。

import cv2
import json

cv2は、OpenCVライブラリーのモジュールです。cv2モジュールは、画像処理を行うために使用します。

jsonは、Python標準ライブラリーのモジュールです。jsonモジュールは、Jsonを扱うために使用します。

OpenCVライブラリーは、事前に導入しておきます。

 

画像ファイルの読み込み

元画像は Canva で事前に作成し、ファイルにダウンロードしました。

サイズを変更する元画像ファイルの読み込み処理は、以下の部分です。

# get original image file
original = 'data/idm_icon.png'
img = cv2.imread(original)
height = img.shape[0]
width = img.shape[1]
print('original file =', original, ',height =', height, ',width =', width)

2行目で、読み込むイメージ・ファイルを指定しています。汎用性を考えると、ファイル名は実行時の引数にする方が良いのですが、まだそこまでできていません。

3行目で、imgモジュールを使ってイメージ・ファイルを読み込み、imgという変数に割り当てています。

4行目から6行目までは、確認のために、ファイル名とイメージの高さ・幅を出力させています。

 

種類の指定

作成するイメージのサイズと、jsonファイルの内容について、以下の部分で定義しています。

この部分も、ハード・コーディングではなく jsonファイルなどで外出しにした方が良いのですが、まだそこまでできていません。

# resize pattern and contents.json images
pattern = {20:(1,2,3),29:(1,2,3),40:(1,2,3),60:(2,3),76:(1,2),83.5:(2,0)}
jsonimg = {'iphone':((20,2),(20,3),(29,1),(29,2),(29,3),(40,2),(40,3),(60,2),(60,3),(76,2)),
           'ipad':((20,1),(20,2),(29,1),(29,2),(40,1),(40,2),(76,1),(76,2),(83.5,2))}

2行目で、イメージ・ファイルの種類を、patternという辞書の変数にしています。

キーの部分が基本サイズで、例えば「20」は「20px x 20px」を表しています。

値の部分は基本サイズの倍率で、タプルで定義しています。例えば「(1,2,3)」は「1倍、2倍、3倍」の3つが有ることを表しています。

つまり、「20px x 20px」、「40px x 40px」、「60px x 60px」の3つが有ることになります。

最後の「83.5px x 83.5px」は、2倍が1つしかないので、本当なら「(2)」でいいと思うのですが、後の処理で「(2)」ではタプルとみなしてくれなかったので、ダミーの「0」を追加しています。

何故要素が一個だとタプルとみなしてくれないのかについては、調べられてません。

3行目から4行目までは、jsonファイルに記述する内容を、jsonimgという辞書変数にしています。

キーの部分が機種で、iPhoneiPadが有ります。

値の部分が種類で、タプルで定義しています。

例えば、最初の「'iphone':((20,2)」の部分は、iPhoneには 20px x 20px の 2倍の画像(40px x 40px)が有ることを表しています。

 

画像ファイルの作成

前述したpatternに沿って、画像をリサイズしファイルに書き出す部分は、以下のようになりました。

# resize image to several size
for size in pattern.keys():
    list = pattern[size]
    for scale in list:
        if scale > 0:
            resized_img = cv2.resize(img, (int(size*scale), int(size*scale)))
            # write image to file
            imgfile = 'data/appicon/Icon-App-' + str(size) + 'x' + str(size) + '@' + str(scale) + 'x.png'
            cv2.imwrite(imgfile, resized_img)

2行目で、pattern辞書にあるキーを size変数に取り出し、キーの分だけ繰り返します。

3行目で、size(キー)に対応した値を、list変数に取得します。

4行目で、listにあるリストの要素を scale変数に取り出し、要素数分処理を繰り返します。

5行目で、scaleの値が「0」で無ければ処理をします。これは、前述したように、「0」はダミーで追加したものなので、処理対象外とします。
6行目で、cv2モジュールの resize関数を使って、元画像をリサイズします。

リサイズの指定は、cv2.resize(元画像, (幅, 高さ)) ですので、幅と高さをそれぞれ size x scale で算出して int型に変換して入れています。

8行目で、書き出す画像ファイルの名前をセットしています。

9行目で、cv2モジュールの imwrite関数を使って、画像をファイルに書き出しています。

 

jsonファイルの作成

画像ファイルと一緒に存在する Contents.jsonファイルを作成します。

Contents.jsonの中を見ると、以下のような構造で記述されています。

{キー("images"):値([{"idiom": "機種","size": "PXxPX","scale": "nx",
            "filename": "Icon-App-PXxPX@nx.png"},{・・・},・・・]),
 キー("info"):値({"version": 1,"author": "xxxxxxxx"})}

 

pythonプログラムは、以下の部分になります。

# create contents.json file
jsonfile = 'data/appicon/Contents.json'
json_dict = {}
img_list = []
for device in jsonimg.keys():
    for ss in jsonimg[device]:
        img_dict = {}
        img_dict['idiom'] = device
        img_dict['size'] = str(ss[0]) + 'x' + str(ss[0])
        img_dict['scale'] = str(ss[1]) + 'x'
        img_dict['filename'] = 'Icon-App-' + str(ss[0]) + 'x' + str(ss[0]) + '@' + str(ss[1]) + 'x.png'
        img_list.append(img_dict)
json_dict['images'] = img_list
info_dict = {}
info_dict['version'] = 1
info_dict['author'] = 'xxxxxxxx'
json_dict['info'] = info_dict
json_file = open(jsonfile, 'w')
json.dump(json_dict, json_file, indent=4)
json_file.close()

2行目で、jsonファイルの名前をセットしています。

3行目から4行目までで、json_dictという辞書と、img_listというリストを定義します。

5行目で、jsonimgという辞書からキーの数分繰り返します。キーとしては、「iphone」と「ipad」の 2つで、deviceという変数にセットします。

6行目で、ぞれぞれのキーの要素数分繰り返します。繰り返しとしては、「iphone」が 10個で、「ipad」が 9個で、ssというリストにセットします。

7行目から11行目までで、img_dictという辞書に、「idiom」「size」「scale」「filename」のキーと、device変数やssリストから作成した要素を追加しています。

12行目は、作成したimg_dict辞書を、img_listリストに追加します。

13行目は、json_dict辞書に「images」のキーと、作成したimg_listリストを要素として追加します。

14行目から17行目までで、info_dictという辞書を作成し、json_dict辞書に「info」のキーと、作成したinfo_dict辞書を要素として追加します。

18行目は、jsonファイルを、書き出しモードでopenします。

19行目は、jsonモジュールのdump関数を使って、テキストとして書き出します。

その際、「indent=4」を指定しておくことにより、フォーマットされて見やすく書き出されます。

20行目は、jsonファイルを、closeします。

結果として、以下のような jsonファイルが書き出されます。(抜粋)

{
    "images": [
        {
            "idiom": "iphone",
            "size": "20x20",
            "scale": "2x",
            "filename": "Icon-App-20x20@2x.png" 
        }, 
        {
            "idiom": "iphone",
            "size": "20x20",
            "scale": "3x",
            "filename": "Icon-App-20x20@3x.png"
        },
        ・・・
        {
            "idiom": "ipad",
            "size": "20x20",
            "scale": "1x",
            "filename": "Icon-App-20x20@1x.png"
        },
        ・・・
    ],
    "info": {
        "version": 1,
        "author": "xxxxxxxx"
    }
}

 

 

Pythonの勉強としてプログラミングをしてみましたが、iPhoneiPadの新機種に対応して、Pythonプログラムをメンテナンスしていくのは面倒です。

なので実際は、前述したWeb上で無料で画像ファイルを作成してくれるところを使用していくつもりです。