Menu

【ニコニコ動画】令和版「Excelで長門有希」技術解説とソースコード公開【Python】

2021年5月14日に公開(2021年6月5日に更新)

ニコニコ動画の伝説「Excelで長門有希」

Pythonで書いてみました。ゆっくりドット絵が完成していくのが見ていてとても面白いです!Excelをリアルタイムで動かしつつ、完全Pythonで実行している方が見つからなかったので、プログラムを組んでみました。プログラムはもちろん自由に使ったり改変してもいいので、色んな動画を皆さんも作ってくれたらうれしいです!

偉大な本家様はこちらです。私が子供のころに「プログラミングってすげー!」と思ったきっかけのひとつです。当時はこれが自力で作れるようになるなんて全く思ってませんでした笑

ここから下は技術的な面での解説とソースコードを記載します。

OpenPyXL vs pywin32

Pythonでエクセルを操作する際に使われるライブラリは主に「OpenPyXL」と「pywin32」がありますが、今回の動画では「pywin32」を採用しました。基本的にRPAや業務の自動化ではだいたい「OpenPyXL」が選択されると思いますが、今回のアプリの目的にはそぐわなかったので却下しました。それぞれのメリットとデメリットについて、使用してみた感想を交えながら解説します。

OpenPyXLの特徴

  • メリット
    • エクセルのデータを直接操作するので動作が早い
      • 記述方法がPythonの文法にある程度最適化されている
      • エクセルを持っていなくても使える
  • デメリット
    • 稀にエクセルと違う動作を行うものがある
      • 使えない機能がある(とはいっても、基本的な操作には困りません)

pywin32の特徴

  • メリット
    • エクセルのソフト自体に操作を指示するので、基本的にエクセルと同じ動作をする
    • VBAの知識がある人は理解しやすく、ソースコードも移行しやすい
  • デメリット
    • 動作が遅い(COMオブジェクトを介してエクセル操作することが原因?)
    • Pythonで記述するにはごちゃごちゃする(ほぼVBAそのもの)
    • エクセルを持っていないと使えない

結論

動作が早くて基本操作が十分にできる「OpenPyXL」以外を選択することはあんまりないと思います。しかし、「pywin32」にはエクセルのソフトを起動してリアルタイムに処理結果を反映させるという点では圧倒的魅力とロマンがあります。データ分析などの仕事では絶対必要ない用途…!
エクセルをおもちゃにしたい方には「pywin32」は最適な選択肢です。

ちなみに、最近では「xlwings」という新しいライブラリも登場したようで、エクセルでの遊びがより一層面白くなりそうな気がしています。
誰か「xlwings」使ってエクセルで遊んでみてほしいです。動画みてみたいです…!

ソースコード

  • IMG_FILE:エクセルに出力したい画像のパスに変更
  • IMG_RATE:全体のドット数の調整に使ってください。1.0が拡大縮小なしです
  • CELLS_WIDTHCELLS_HEIGHT:セルの横幅と高さです。理論上は横幅「px * 0.118」、高さ「px * 0.75」のはずなのですが、うまくいかないので各自調整してください。
  • OUTPUT_NAME:出力するエクセルのファイル名を指定してください。カレントディレクトリに保存されます。
import os
import time
import win32gui
import win32con
import win32com.client
from PIL import Image

# 出力したい画像の場所
IMG_FILE = "./668076_mod.jpg"
# 画像の拡大率(1.0:標準サイズ)
IMG_RATE = 0.2

# (セルが正方形になるよう細かく調整する必要があります)
# セルの横幅
CELLS_WIDTH = 11 * 0.07
# セルの高さ
CELLS_HEIGHT = 11 * 0.65

# 出力するエクセルのファイル名
OUTPUT_NAME = "outputSaveAs.xlsx"


# 画像データを読み込む
img_pil = Image.open(IMG_FILE)

# 一定の倍率で拡大縮小
w = int(img_pil.width * IMG_RATE)
h = int(img_pil.height * IMG_RATE)
img_resize = img_pil.resize( (w, h) )

# リサイズ後の画像のサイズを取得
width, height = img_resize.size

# モードの取得
img_mode = img_resize.mode

# エクセルの起動
xl = win32com.client.Dispatch('Excel.Application')
win32gui.ShowWindow(xl.hwnd, win32con.SW_MAXIMIZE)

# エクセル画面の表示
xl.Visible = True

# 各種エクセル画面項目を非表示
xl.DisplayStatusBar = False
xl.DisplayFunctionToolTips = False
xl.DisplayExcel4Menus = False
xl.DisplayFormulaBar = False
xl.ShowDevTools = False
xl.ShowToolTips = False

# シートを作成してアクティブ化
wb = xl.Workbooks.Add()
ws = xl.Sheets("Sheet1")
ws.Activate
# シート名の変更
ws.Name = "Image"

# 列の幅と高さを変更
# 画像の幅のドット分、セルの横幅を調整する
ws.Range(ws.Columns(1), ws.Columns(width)).ColumnWidth = CELLS_WIDTH
# 画像の高さのドット分、セルの高さを調整する
ws.Range(ws.Rows(1), ws.Rows(height)).RowHeight = CELLS_HEIGHT

# 20秒待つ
time.sleep(20)

# 画像のドット情報を配列として取得
imgdata = img_resize.getdata()

# 画像の全てのドットへアクセスする
for y in range(height):
    ycache = y * width
    for x in range(width):

        if img_mode == "RGBA" :
            # ドットのRGBA情報の取得
            r,g,b,a = imgdata[ycache + x]
            # アルファ値(透明度)が一定以上の場合、有色と判断する(0:透明、255:不透明)
            if a > 128:
                # セルに色をつける(R+Gx256+Bx256x256)
                ws.cells(y+1, x+1).Interior.Color = r + g*256 + b*256*256
        else :
            # ドットのRGB情報の取得
            r,g,b = imgdata[ycache + x]
            # セルに色をつける(R+Gx256+Bx256x256)
            ws.cells(y+1, x+1).Interior.Color = r + g*256 + b*256*256

    # 1秒待つ
    time.sleep(1)

# 10秒待つ
time.sleep(10)

# カレントディレクトリにエクセルワークブックを保存する
wb.SaveAs(f"{os.getcwd()}\\" + OUTPUT_NAME, FileFormat = 51)

# 20秒待つ
time.sleep(20)

# エクセルワークブックを閉じる
wb.Close()
# エクセルを終了する
xl.Quit()

エラーが出たり、上手く動かない場合はコメントください!
動画作ってみたよって人がいたら見に行きたいです。魔改造してくれたらうれしいです!!!

参考

©2023 Maya Hanada