夏休みなので小学生とPythonを勉強してみた - 課題6 写真の整理

小学6年生の長男が夏休みの自由研究にPythonを勉強したいというので付き合ってみました。
どこから手を付けていけばわからないので、いくつか課題を出し、それを実現するコードを一緒に書くということを繰り返しました。

この記事では、「課題6 写真の整理」について扱います。その他の課題については下記の記事をご覧ください。

* [夏休みなので小学生とPythonを勉強してみた - 課題1 FizzBuzz - Qiita](https://qiita.com/shakiyam/items/b8366fde704639abb63d)
* [夏休みなので小学生とPythonを勉強してみた - 課題2 掛け算九九 - Qiita](https://qiita.com/shakiyam/items/0e4153fe7c610b3e8e49)
* [夏休みなので小学生とPythonを勉強してみた - 課題3 掛け算ゲーム - Qiita](https://qiita.com/shakiyam/items/4b4df04369914df3baa6)
* [夏休みなので小学生とPythonを勉強してみた - 課題4 じゃんけん - Qiita](https://qiita.com/shakiyam/items/05c756b4726c941c1c5c)
* [夏休みなので小学生とPythonを勉強してみた - 課題5 サイコロの目の出る確率 - Qiita](https://qiita.com/shakiyam/items/9e0c5548efd0952ab250)
* [夏休みなので小学生とPythonを勉強してみた - 課題7 掛け算ゲームWeb版 - Qiita](https://qiita.com/shakiyam/items/3945a224a8774c332220)
* [夏休みなので小学生とPythonを勉強してみた - 課題8 掲示板 - Qiita](https://qiita.com/shakiyam/items/c3aa6425ff42b75d3310)
* [夏休みなので小学生とPythonを勉強してみた - 課題9 顔の検出 - Qiita](https://qiita.com/shakiyam/items/110b4819b12124587d80)
* [夏休みなので小学生とPythonを勉強してみた - 課題10 顔の認識 - Qiita](https://qiita.com/shakiyam/items/0125dd5db15976d5520e)

コードは[GitHub](https://github.com/shakiyam/python4kids)にて公開しています。

課題6-1 ファイル一覧
--------------------

### 指示 ###

カレント・ディレクトリ直下のsrcというディレクトリにある写真(JPEGファイル)の一覧を表示しましょう。

### ヒント ###

globモジュールは、ディレクトリのワイルドカード検索からファイルのリストを生成するための関数を提供しています。

* [10.2. ファイルのワイルドカード表記 — Python チュートリアル](https://docs.python.org/ja/3/tutorial/stdlib.html#file-wildcards)
* [glob — Unix 形式のパス名のパターン展開 — Python 標準ライブラリ](https://docs.python.org/ja/3/library/glob.html)

os.pathモジュールは、パス名を操作する便利な関数が実装されています。os.path.join関数はパスを結合します。

* [os.path — 共通のパス名操作 — Python 標準ライブラリ](https://docs.python.org/ja/3/library/os.path.html)

### 回答例 ###

```python
import glob
import os

SRC_PATH = 'src'

for image_file in glob.glob(os.path.join(SRC_PATH, '*.jpg')):
    print(image_file)
```

課題6-2 ファイル名変更
----------------------

### 指示 ###

srcというディレクトリにある写真に連番で名前を付け、destというディレクトリにコピーしましょう。

### ヒント ###

シーケンスにわたるループを行う際、enumerate関数を使うと、要素のインデックスと要素を同時に取り出すことができます。

* [5.6. ループのテクニック — Python チュートリアル](https://docs.python.org/ja/3/tutorial/datastructures.html#looping-techniques)

shutilモジュールは、ファイルやファイルの集まりに対する高水準の操作方法を多数提供します。shutil.copyfile関数はファイルをコピーします。

* [10.1. OSへのインタフェース — Python チュートリアル](https://docs.python.org/ja/3/tutorial/stdlib.html#operating-system-interface)
* [shutil — 高水準のファイル操作 — Python 標準ライブラリ](https://docs.python.org/ja/3/library/shutil.html#module-shutil)

### 回答例 ###

```python
import glob
import os
import shutil

SRC_PATH = 'src'
DEST_PATH = 'dest'
RENAME_FORMAT = 'image'

for i, image_file in enumerate(glob.glob(os.path.join(SRC_PATH, '*.jpg')), 1):
    renamed_file = f'{RENAME_FORMAT}_{i:03}.jpg'
    print(f'{os.path.basename(image_file)} -> {renamed_file}')
    shutil.copy(image_file, os.path.join(DEST_PATH, renamed_file))
```

課題6-3 サムネイル作成
----------------------

### 指示 ###

srcというディレクトリにある写真に連番で名前を付け、destというディレクトリにコピーするとともにサムネイルを作成しましょう。
サムネイルは縦横それぞれ100ピクセル以内になるように作成します。

### ヒント ###

画像を扱うライブラリはいろいろありますが、OpenCVを使うことにします。

画像ファイルを読み込むにはcv2.imread関数を使います。

* [画像を読み込む — OpenCV-Python Tutorials](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_image_display/py_image_display.html#id3)

画像の大きさはimg.shapeによって取得できます。縦ピクセル数、横ピクセル数、チャンネル数が返されるので、最初の2つを使います。

* [画像の属性情報の取得 — OpenCV-Python Tutorials](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_core/py_basic_ops/py_basic_ops.html#id4)

画像のサイズ変更は、cv2.resize関数を使います。

* [スケーリング(拡大・縮小) — OpenCV-Python Tutorials](http://lang.sist.chukyo-u.ac.jp/classes/OpenCV/py_tutorials/py_imgproc/py_geometric_transformations/py_geometric_transformations.html#id4)

画像を保存するには、cv2.imwrite関数を使います。

* [画像を保存する — OpenCV-Python Tutorials](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_image_display/py_image_display.html#id5)

### 回答例 ###

```python
import glob
import os
import shutil

import cv2

SRC_PATH = 'src'
DEST_PATH = 'dest'
RENAME_FORMAT = 'image'
THUMBNAIL_SIZE = 100

for i, image_file in enumerate(glob.glob(os.path.join(SRC_PATH, '*.jpg')), 1):
    renamed_file = f'{RENAME_FORMAT}_{i:03}.jpg'
    print(f'{os.path.basename(image_file)} -> {renamed_file}')
    shutil.copy(image_file, os.path.join(DEST_PATH, renamed_file))

    image = cv2.imread(image_file, cv2.IMREAD_COLOR)
    height, width = image.shape[:2]
    thumbnail_file = f'{RENAME_FORMAT}_{i:03}_thumb.jpg'
    scale = min(THUMBNAIL_SIZE / height, THUMBNAIL_SIZE / width)
    if scale < 1:
        thumbnail_image = cv2.resize(image, dsize=None, fx=scale, fy=scale)
        cv2.imwrite(
            os.path.join(DEST_PATH, thumbnail_file),
            thumbnail_image)
    else:
        shutil.copy(image_file, os.path.join(DEST_PATH, thumbnail_file))
```