質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.64%

Pythonで画像のリサイズとテキストの挿入を行うプログラム

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 391

前提・実現したいこと

Pythonで
『指定した画像に対し、リサイズとテキストの挿入を行う』
プログラムを作成しようとおります。

そのプログラムの実行中に以下のエラーが発生してしまい、画像が作成されません。

発生している問題・エラーメッセージ

C:\Users\OI\Desktop\Python>python prog.py
Traceback (most recent call last):
  File "prog.py", line 125, in <module>
    main()
  File "prog.py", line 109, in main
    for row in reader:
  File "C:\Users\OI\AppData\Local\Programs\Python\Python37\lib\csv.py", line 111, in __next__
    self.fieldnames
  File "C:\Users\OI\AppData\Local\Programs\Python\Python37\lib\csv.py", line 98, in fieldnames
    self._fieldnames = next(self.reader)
UnicodeDecodeError: 'shift_jis' codec can't decode byte 0xef in position 0: illegal multibyte sequence

該当のソースコード

import csv
import matplotlib.pyplot as plt
import numpy as np
import os
from PIL import Image, ImageDraw, ImageFont


SETTING_CSV = "setting.csv"
INPUT_DATA_DIR = "./input"
OUTPUT_DATA_DIR = "./output"
IMG_Q = 90
IMG_SIZE = (780, 428)
TEXT_SIXE = (750, 200)
TEXT_MARGIN = 30
MAX_FONT_SIZE = 100
FONT_PATH = "./fonts/migmix-1p-bold.ttf"
TEXT_COLORS = (('black', '#000000', (255, 255, 255, 192)),
               ('white', '#FFFFFF', (0, 0, 0, 192)))


def make_eye_catch(img_path, text):
    # 画像のファイル名を取得
    file_name, ext = os.path.splitext(img_path)
    # \n(改行指定)でテキストを分割
    text = text.split(r"\n")

    # 先に文字の描画領域決定。合わせて下記を計算しておく
    # フォントサイズfont_size, 描画幅area_width, 描画高さarea_height, lines_size
    for font_size in range(MAX_FONT_SIZE, 9, -1):

        # フォント
        font = ImageFont.truetype(FONT_PATH, font_size)

        # 各行の[幅、高さ]を計算
        lines_size = [list(font.getsize(t)) for t in text]

        # 幅の最大と高さの合計
        area_x = np.max(lines_size, 0)[0]
        area_y = np.sum(lines_size, 0)[1]

        if(area_x <= TEXT_SIXE[0] and area_y <= TEXT_SIXE[1]):
            break

    # 画像読み込み、サイズ取得
    img = Image.open(os.path.join(INPUT_DATA_DIR, img_path))
    original_img_size = img.size

    # 縮小
    if (original_img_size[0] / original_img_size[1]) \
            >= (IMG_SIZE[0] / IMG_SIZE[1]):
        # 目標より横長なら、縦横比を維持して、縦を目標まで縮める
        img.thumbnail((original_img_size[0], IMG_SIZE[1]))
        thumbnail_size = img.size
        # 横幅を切り取るための計算をする
        crop_left = int((thumbnail_size[0] - IMG_SIZE[0]) / 2)
        crop_upper = 0
        crop_right = crop_left + IMG_SIZE[0]
        crop_lower = IMG_SIZE[1]
    else:
        # 目標より縦長なら、縦横比を維持して、横を目標まで縮める
        img.thumbnail((IMG_SIZE[0], original_img_size[1]))
        thumbnail_size = img.size
        # 縦幅を切り取るための計算をする
        crop_left = 0
        crop_upper = int((thumbnail_size[1] - IMG_SIZE[1]) / 2)
        crop_right = IMG_SIZE[0]
        crop_lower = crop_upper + IMG_SIZE[1]

    # 計算した縦横で切り取る
    img = img.crop((crop_left, crop_upper, crop_right, crop_lower))

    # 背景の塗りつぶしと文字書き込み準備
    rectangle_y = area_y + TEXT_MARGIN
    rectangle_top = int((IMG_SIZE[1]-rectangle_y)/2)

    # 白黒2色分画像を作成
    for (label, fg_color, bg_color) in TEXT_COLORS:
        # 塗りつぶし領域作成
        rectangle_img = Image.new('RGBA', img.size)
        draw = ImageDraw.Draw(rectangle_img)
        draw.rectangle((0, rectangle_top, IMG_SIZE[0],
                        rectangle_top+rectangle_y), bg_color)

        # 塗りつぶし
        org_img = img.convert('RGBA')
        new_img = Image.alpha_composite(org_img, rectangle_img)

        draw = ImageDraw.Draw(new_img)
        _y = int((IMG_SIZE[1]-area_y)/2)
        for (t, line) in zip(text, lines_size):
            _x = int((IMG_SIZE[0]-line[0])/2)
            draw.text((_x, _y), t, font=font, fill=fg_color)
            _y += line[1]

        new_img = new_img.convert("RGB")
        res_file_name = file_name + "_" + label + ".jpg"
        new_img.save(os.path.join(OUTPUT_DATA_DIR, res_file_name),
                     quality=IMG_Q)
        plt.figure()
        plt.imshow(new_img)


def main():
    input_data_list = []

    # csvファイルの文字コードがSJISの時はencoding="shift_jis"を指定する
    with open(SETTING_CSV, "r", encoding="shift-jis") as csv_file:
        reader = csv.DictReader(csv_file)
        for row in reader:
            input_data_list.append([row["path"], row["sentence"]])

    # もしoutputフォルダが無かったら作る
    if not os.path.exists(OUTPUT_DATA_DIR):
        os.makedirs(OUTPUT_DATA_DIR)

    # アイキャッチ画像の生成
    for img_path, text in input_data_list:
        make_eye_catch(img_path, text)
        info_msg = "[INFO] {0}の処理が完了しました"
        print(info_msg.format(os.path.join(INPUT_DATA_DIR, img_path)))
    print("[INFO] Finish!!")


if __name__ == "__main__":
    main()

※画像の名前・挿入したい文字列を指定したsetting.csvというファイルを利用しておりますが、そちらには問題はないと思います。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

エラーが発生しているのはソースのこの場所で、ここは setting.csv を読み込んでいる場所です。

  File "prog.py", line 125, in <module>
    main()
  File "prog.py", line 109, in main
    for row in reader:

shift-jis とは思えない値が入っていたよ。

UnicodeDecodeError: 'shift_jis' codec can't decode byte 0xef in position 0: illegal multibyte sequence


というエラーが出ています。 
0バイト目が 0xEF であることが原因とありますが、ファイルの先頭が0xEFになっているということは、BOM付きのUTF-8の可能性が高いです。

ということで、問題無いとおっしゃっていますが、setting.csvのエンコーディングがshift-jisでないことが問題だろうと思います。

  1. setting.csvのエンコーディングをshift-jisに変更する。
  2. 読み込むときのエンコーディングの指定を shift-jis でなく、utf-8-sig に変更する。

あたりで解決するのではないかと思います。 

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る