Pythonでいろいろやってみる

Pythonを使った画像処理や機械学習などの簡単なプログラムを載せています。

国旗の色比率を円グラフにする

 TBSのクイズ番組「東大王」で、国旗の色比率を円グラフにしたものを見て国旗を当てる、というクイズをやっていたのを見て同じようなグラフを作ってみました。   

環境
  • windows10 home
  • Anaconda 3/ jupyter notebook 5.6.0
  • Python 3.8.8
  • OpenCV 4.5.5
  • pandas 1.4.0
  • matplotlib 3.5.1
準備

 国旗画像は国旗 | 地図に使えるフリー素材.jpからダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリに保存しました。

コード

 まず、OpenCVで画像を読み込み全画素の色リストを作ります。その際に色情報を処理しやすいようBx1000000+G*1000+Rと1つの数字にまとめます。collections.Counterで色の出現回数を求め、出現回数(ピクセル数)と色情報をpandasのデータフレーム化します。ピクセル数が全体の1%未満の色をノイズとして削除し降順に並べ替えます。ピクセル数と色のリストを作り、matplotlibで円グラフを作ります。
 まず、UAEの国旗でやってみます(ファイル名 'UAE.png')

import cv2
import collections
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 国旗画像読み込みと、サイズの取得
img = cv2.imread('UAE.png')
h, w, _ = img.shape

# 全画素の色リストを作成
# 色情報は B*1000000 + G*1000 + R に変換し
# 0埋めした9桁の文字列に  
color_list = []
for y in range(h):
    for x in range(w):
        color_list.append(str(img[y, x, 0]*1000000 +
                          img[y, x, 1]*1000 + 
                          img[y, x, 2]).zfill(9))
        
# 出現回数(ピクセル数)をカウントする        
counter = collections.Counter(color_list)

# ピクセル数と色をpandasデータフレーム化
df = pd.DataFrame(data=[list(counter.values()), list(counter.keys())],
                 index = ['pixels', 'BGR']).T

# ピクセル数が全体の1%未満の行を削除(ノイズとみなす)
df2 = df[df['pixels'] > h*w*0.01]

# 降順に並べ替え
df3 = df2.sort_values(by=['pixels'], ascending=False)

# ndarrayに変換し一行ずつ読み出しリストに追加
# 色情報は9桁の文字列なので3文字ずつ読み出し 
# 255で割って0~1の数字に変換
# 順番をBGR→RGBに変更
x_list =[]
rgb_list =[]
for i in df3.to_numpy():
    x_list.append(i[0])  
    rgb_list.append([int(i[1][6:9])/255, 
                     int(i[1][3:6])/255,
                     int(i[1][0:3])/255])

# 円グラフの作成
plt.pie(x_list, 
        colors=rgb_list, 
        wedgeprops={'linewidth': 1, 'edgecolor':'black'}, 
        counterclock=None, 
        startangle=90)
plt.show()
実行結果

国旗の色比に応じた円グラフが作成されます。

もとの国旗画像はこちら。


別の国旗を試します。
スリランカの国旗(ファイル名 'Sri_Lanka.png' )を使います。

実行結果

国旗の色比に応じた円グラフが作成されます。

もとの国旗画像はこちら。

以下のサイトを参考にさせていただきました

子供の落書き帳 Renaissance > [pandas]特定の条件を満たす行を削除する
note.nkmk.me > Pythonで文字列・数値をゼロ埋め(ゼロパディング)

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村

徐々に画像がはっきりするgif動画

 画像が徐々にハッキリしていくgif動画を作ります。画像処理ライブラリPillowのeffect_spreadで画像をぼかすことができますが、そのぼかし具合を少しずつ減らすことで画像を徐々にハッキリさせます。   

関連記事

少しずつ画像がはっきりする動画

環境
  • windows10 home
  • Anaconda 3/ jupyter notebook 5.6.0
  • Python 3.8.8
  • Pillow 8.2.0
準備

 以下の画像ファイルをフリー写真素材ぱくたそからダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリにファイル名'hongkong.jpg'で保存しました。

コード
from PIL import Image

im = Image.open('hongkong.jpg')  

# gifファイル作成用リスト
images = []

for i in range(50, -1, -1):
    # effect_spread処理
    spread = im.effect_spread(i)     
    # gifファイル作成用イメージリストにフレームを追加
    images.append(spread)
        
# gif動画保存
images[0].save('spread.gif', save_all=True, append_images=images[1:], 
               optimize=False, duration=150, loop=0)     

実行結果

ボケていた画像が徐々にハッキリするgif動画が作られます。

以下のサイトを参考にさせていただきました

Pythonの文法メモ > 【Pillow】Image.effect_spreadによる画像のぼかし

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村

パソコンの内蔵カメラで動画を撮る

 パソコンの内蔵カメラで動画を撮ります。OpenCVの cv2.VideoCapture で引数に動画ファイル名を指定した場合は、動画ファイルが開かれビデオキャプチャーオブジェクトに入力されますが、デバイス番号でカメラを指定すると、ビデオキャプチャーオブジェクトにカメラの画像が入力されます。デバイス番号は内蔵カメラの場合0、増設したUSBカメラは1,2・・・のように指定します。

環境
  • windows10 home
  • Anaconda 3/ jupyter notebook 6.4.6
  • Python 3.8.12
  • OpenCV 4.5.5
コード

 実行すると内蔵カメラから5秒間画像を取り込み、動画ファイルに保存されます。動画のサイズ・レート(fps)はカメラから取得し、その値を用いています。ファイル名は撮影時の時刻を設定しています。      

import datetime
import cv2

# 撮影時間
length = 5

# 現在時刻の文字列を取得
now = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')

# 内蔵カメラをビデオキャプチャーオブジェクトに設定
cap = cv2.VideoCapture(0)

# カメラのFPS、幅、高さ取得
fps = cap.get(cv2.CAP_PROP_FPS)
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 保存用動画ファイルのフォーマット設定
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') 
out = cv2.VideoWriter(now+'.mp4', fourcc, fps, (w, h)) 

# 撮影時間(秒) X フレームレートの回数画面取得し
# ビデオライターオブジェクトに記録
for i in range(int(length*fps)):
    ret, frame = cap.read()
    out.write(frame)

# キャプチャーオブジェクトのリリース    
cap.release()
# ビデオライターオブジェクトのリリース    
out.release()     
実行結果

内蔵カメラで5秒間録画されたmp4ファイルが保存されます。

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村

画像でモザイク画を作る(フォトモザイク)

 画像をモザイク画に変換します。様々な色合いの画像を用意し15x10ピクセルに縮小、置き換え用画像にします。変換する画像を15x10ピクセルの碁盤目状のセルに分割し各セルのBGR平均値を出し、BGR平均値の近い置き換え用画像に置き換えます。

環境
  • windows10 home
  • Anaconda 3/ jupyter notebook 6.4.6
  • Python 3.8.12
  • numpy 1.21.5
  • OpenCV 4.5.5
準備

 変換する画像はフリー写真素材ぱくたそからダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリにフォルダ'photos'を作成しその中に保存しました。

mamechi.jpg

 置き換え用画像は色味の異なる画像30枚、同じくフリー写真素材ぱくたそからダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリにフォルダ'images'を作成しその中に保存しました。

コード

 変換する画像の15x10ピクセルに分割し、各セルのB,G,R値の平均を求めます。置き換え用画像30枚のB,G,R値と比較し、最も近い置き換え用画像を選びます。近さは
B値の差の二乗 + G値の差の二乗 + R値の差の二乗
により求め、この値が最も小さい置き換え用画像を選びます。
 

import os
import cv2
import numpy as np

# 置き換え用画像格納リスト
image_list = []

# 置き換え用画像BGR平均値格納リスト
bgr_list = []  

# セルのサイズ
xpitch = 15
ypitch = 10

# imagesフォルダ内のファイルのリスト生成
files = os.listdir('images/')  
# imagesフォルダからファイル名を1つずつ取り出して処理
for i in files:     
    pic = cv2.imread('images/'+i)    
    resize = cv2.resize(pic, (xpitch, ypitch))
    image_list.append(resize)
    bgr_list.append(np.mean(resize, axis=(1,0)))
    
# 変換する画像を読み出し、セルサイズの整数倍にリサイズ
# 同じサイズの黒画像(replace)を変換後画像用に作成
pic = cv2.imread('mamechi.jpg')
height, width, channel = pic.shape
resize = cv2.resize(pic, (width//xpitch*xpitch, height//ypitch*ypitch))
replace = np.zeros((height//ypitch*ypitch, width//xpitch*xpitch, 3), np.uint8)

# 変換する画像をセルサイズに分割して読み出し、各セルのBGR平均値を算出
# 置き換え用画像と比較し、最も近い画像を選択、変換後画像に張り付ける
for y in range(0, height//ypitch*ypitch, ypitch):
    for x in range(0, width//xpitch*xpitch, xpitch):
        part = pic[y:y+ypitch, x:x+xpitch]
        bgr = np.mean(part, axis=(1,0))
        min_distance = 195075
        for i in range(len(image_list)):
            j = image_list[i]
            k = bgr_list[i]
            distance = ((bgr[0]-k[0])**2+
                        (bgr[1]-k[1])**2+
                        (bgr[2]-k[2])**2)
            if distance<min_distance:
                min_distance = distance
                choice = j  
        replace[y:y+ypitch, x:x+xpitch] = choice

# 変換後画像の保存
cv2.imwrite('replace.jpg', replace)   
実行結果

モザイク画に変換された画像(replace.jpg)が保存されます


もう少しモザイク画を滑らかにするために、①xpitch = 9 ypitch = 6に変更②モザイク用画像を30→47枚に増やしました。だいぶ滑らかになりました。

以下のサイトを参考にさせていただきました

Pythonの文法メモ > 【OpenCV】画像読み出しとサイズ・画素情報取得、切り抜き、貼り付け、チャネル操作
Pythonの文法メモ > 【OpenCV】画像サイズを変更するresize

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村

パソコンの画面を録画する

 パソコンの画面を録画して動画ファイルに保存します。ライブラリPyAutoGUIを使うとパソコン画面のスクリーンショットを取得できます。スクリーンショット画像を連続して取得し、OpenCVで動画ファイルに書き込むことで画面操作を録画します。PyAutoGUIのスクリーンショットではマウスカーソルが消えてしまうので 、PyAutoGUIでマウスの現在位置を取得しその位置にマウスカーソルの絵を描いています。

環境
  • windows10 home
  • Anaconda 3/ jupyter notebook 6.4.6
  • Python 3.8.12
  • pyautogui 0.9.53
  • numpy 1.21.5
  • OpenCV 4.5.5
コード

 パソコンの画面サイズは1920x1080ですが画面下のタスクバーを消したいのでキャプチャーサイズを1920x1030としています。動画のレートは15FPS、録画時間を10秒に設定しています。画像一枚ごとの取得時間を計測しそれが1/FPS(15FPSの場合は0.0667秒)よりも小さい場合、時間合わせのためスリープを入れています。環境によると思いますが私のパソコンでは15FPSで実際の処理時間とトントン(スリープが入ったり入らなかったり)、25FPSでは処理が間に合いませんでした。間に合わない場合、例えば20秒の録画時間が10秒の動画となり早送りになってしまいます。
 また、プログラムを実行して少し経ってから録画を開始するため初めに3秒間スリープしています。  

import time
import pyautogui
import cv2
import numpy as np

# 3秒スリープ
time.sleep(3)

# 動画のフレームレート
fps=15 

# 録画時間(秒)
rec_sec = 10 

# キャプチャー領域
cap_region = (0,0, 1920, 1030)

# キャプチャー画像を格納するリスト
frames = []

#画面をキャプチャーし
for i in range(int(fps*rec_sec)):
    # 処理開始時間の取得
    start = time.perf_counter()
    
    # 画面をキャプチャー
    cap = pyautogui.screenshot(region = cap_region)
    
    # pillow形式からOpenCV形式に変換
    img = cv2.cvtColor(np.array(cap), cv2.COLOR_RGB2BGR)

    # マウス位置を取得しマウスの絵を描画
    mx, my = pyautogui.position()
    pts = np.array([[mx, my], [mx, my+25], [mx+5, my+20],
                    [mx+10, my+28],[mx+14, my+27],[mx+11, my+20],
                    [mx+20, my+20]])
    cv2.polylines(img, [pts], True, 30, thickness=1)
    
    # リストにキャプチャー画像を追加
    frames.append(img)
    
    # 処理終了時間の取得
    end = time.perf_counter()
    
    # 1回の処理時間が1フレームより短い場合、不足分スリープ
    if end-start<1/fps:
        time.sleep(1/fps-(end-start))
        
# 保存用動画ファイルのフォーマット設定
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') 
out = cv2.VideoWriter('capture.mp4', fourcc, fps, (1920, 1030))         

# キャプチャー画像を読み出して出力動画ファイルに追記
for img in frames:        
    out.write(img) 
    
out.release() # 出力動画ファイルをクローズ   
実行結果

画面を録画した動画ファイルが保存されます。
(下はサイズを縮小してアニメーションGIFに変換したもの)
f:id:T_A_T:20220215223547g:plain

以下のサイトを参考にさせていただきました

機械系エンジニアの備忘録 > 【python】デスクトップ画面をキャプチャ(録画)する簡易ソフトを作る
白猫学生のブログ > マウスカーソルの現在の座標の取得方法

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村

動画を鉛筆画風に変換する

 動画を鉛筆画に変換します。画像処理ライブラリOpenCVの cv2.VideoCapture で動画ファイルを読み出し各フレームをcv2.pencilSketchで鉛筆画に変換し動画に保存します。1.5秒までは元動画、1.5秒~3秒はcv2.addWeightedで元動画から鉛筆画に徐々に変化させ、3秒以降を鉛筆画にしています。

環境
  • windows10 home
  • Anaconda 3/ jupyter notebook 6.4.6
  • Python 3.8.12
  • Pillow 9.0.0
  • OpenCV 4.5.5
準備

動画ファイルはMixkitの 'Cute old couple in a park' をダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリにファイル名 ’couple.mp4' で保存しました(動画のフレームサイズは480x270)。このブログにはmp4ファイルが貼れないのでアニメーションgifファイルに変換したものを下に貼ります。

f:id:T_A_T:20220207201630g:plain
couple.mp4(gifファイルに変換したもの)

コード  
from PIL import Image
import cv2
import numpy as np

# gifファイル作成用イメージリスト
images =[]  

# 動画ファイル作成用イメージリスト
frames =[]  

# 動画ファイルのキャプチャー
cap = cv2.VideoCapture('couple.mp4')

# フレームの幅取得
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)

# フレームの高さ取得
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)

# 総フレーム数取得
allframes = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

# 動画ファイルのフレームレート取得
fps = cap.get(cv2.CAP_PROP_FPS)

# 保存用動画ファイルのフォーマット設定
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') 
out = cv2.VideoWriter('pencil.mp4', fourcc, fps, (int(width), int(height))) 

# 動画を1コマずつ取り込んで処理
while(cap.isOpened()): 
    ret, frame = cap.read() # キャプチャー画像の取り込み
    
    if ret==True: # キャプチャー画像がある場合
        
        # 現在時間を取得
        time = cap.get(cv2.CAP_PROP_POS_MSEC)
                
        # 鉛筆画変換
        dst1, dst2 = cv2.pencilSketch(frame)
        
        # 1500msまでは元の画像
        if time<1500: 
            img = frame
            
        # 1500-3000msは元の画像と鉛筆画のブレンド        
        if time>=1500 and time<=3000: 
            alpha = 1-(time-1500)/1500
            beta = 1-alpha 
            img = cv2.addWeighted(frame, alpha, dst2, beta, 0)
            
        # 3000msからは鉛筆画        
        if time>3000: 
            img = dst2
               
        # VideoWriterにフレームを追加
        out.write(img)

        # gifファイル作成用イメージリストにフレームを追加
        images.append(Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)))

    else: # キャプチャー画像がない場合はループ終了 
        break
        
cap.release() # 再生画像をクローズ   
out.release() # 出力動画ファイルをクローズ

# gif動画保存
images[0].save('pencil.gif', save_all=True, append_images=images[1:], 
               optimize=False, duration=1000/fps, loop=0)    
実行結果

鉛筆画に変換された動画が保存されます。
f:id:T_A_T:20220207201830g:plain

以下のサイトを参考にさせていただきました

Emotion Explorer > Python 鉛筆画風 PNGアニメーションを作成
Pythonの文法メモ > 【OpenCV】動画ファイルの読み出しとプロパティ取得、キャプチャー画像の保存

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村

複数の動画に字幕を付けて1つの動画にまとめる

 複数の動画を1つの動画にまとめます。また各動画の説明用字幕を付けます。画像処理ライブラリOpenCVの cv2.VideoCapture で各動画の先頭から3秒まで読みだし、画像処理ライブラリPillowのdraw.textで字幕を付けます。字幕が徐々に現れて徐々に消えるように見せるため、元の動画から読みだした画像とアルファ値を持たせた字幕用画像を、字幕用画像のアルファ値を変えながらPillowのImage.alpha_compositeで合成しています。

環境
  • windows10 home
  • Anaconda 3/ jupyter notebook 6.4.6
  • Python 3.8.12
  • Pillow 9.0.0
  • OpenCV 4.5.5
準備

 Mixkitから3つの動画をダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリにフォルダ'movies'を作りその中にファイル名 ’01.mp4', ’02.mp4', ’03.mp4' で保存しました。
White sand beach and palm trees
Black scorpion walking closeup
Fly over a huge canyon covered in vegetation

コード

 はじめにmoviesフォルダに含まれるファイルをリストにし、動画をひとつづつ読みだします。cv2.VideoCaptureで動画を取り込み、cap.get(cv2.CAP_PROP_POS_MSEC)で現在の時間を調べ3000msまで取り込みます。字幕用画像のアルファ値を
 ~1000ms:0から徐々に増える
 1000~2000ms:一定の値(220)
 2000ms~:0まで徐々に減る
として字幕が表れて消える効果を表現しています。 
 動画読み出しと動画書き出しをOpenCVで行いますが、字幕の書き込みは日本語が描けるPillowで行うためOpenCV⇔Pillowで画像を受け渡す際には画像フォーマットの変換を行います。
 動画はmp4ファイルで書き出すとともに、このブログに貼るためにgifアニメーションも作成しています。      

import os
from PIL import Image, ImageDraw, ImageFont
import cv2
import numpy as np

# moviesフォルダ内のファイルをリストに
files = os.listdir('movies/') 

# gifファイル作成用イメージリスト
images =[]  

# 動画ファイル作成用イメージリスト
frames =[]  

# 字幕のリスト
subtitles =[
    '白い砂浜の南国のビーチ',
    '森を進む黒いサソリ',
    '森に囲まれたロッキー渓谷'
            ]
# 字幕のアルファ値(不透明度)の最大値
amax = 220

# 保存用動画ファイルのフォーマット設定
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') 
out = cv2.VideoWriter('subtitles.mp4', fourcc, 25.0, (384, 216)) 

# 動画ファイルをひとつずつ読みだして処理
for i in range(3):
    movie = files[i]
    subtitle = subtitles[i]
    
    # 動画ファイルのキャプチャー
    cap = cv2.VideoCapture('movies/'+movie)
    
    while(cap.isOpened()): 
        # キャプチャー画像の取り込み
        ret, frame = cap.read() 
        
        # 現在時間を取得
        time = cap.get(cv2.CAP_PROP_POS_MSEC)

        # キャプチャー画像があって3000msec以内の場合
        if ret==True and time<3000: 

            # 384x216にリサイズ
            resize = cv2.resize(frame, (384, 216))
            
            # 画像をcv2形式からPillow形式に変換しアルファチャンネルを追加
            im = Image.fromarray(cv2.cvtColor(resize, cv2.COLOR_BGR2RGB))
            im.putalpha(255)
            
            # 字幕のアルファ値設定
            if time < 1000:
                alpha = int(amax*time/1000)

            elif time > 2000:
                alpha = int(amax*(3000-time)/1000)
                
            else:    
                alpha = amax

            # 字幕用画像に字幕を縁取り文字で描画        
            im2 = Image.new('RGBA', (384, 216), (0, 0, 0, 0))
            draw = ImageDraw.Draw(im2)
            font = ImageFont.truetype('C:\Windows\Fonts\BIZ-UDGothicB.ttc', 24)
            draw.text((40, 180), subtitle, fill=(255, 255, 255, alpha), font=font, 
                      stroke_width=1, stroke_fill=(150, 150, 150, alpha))
                        
            # 動画のフレームと字幕用画像の合成
            comp = Image.alpha_composite(im, im2)
            
            # gifファイル作成用イメージリストにフレームを追加
            images.append(comp)
            
            # Pillow形式からcv2形式に変換
            img = cv2.cvtColor(np.array(comp), cv2.COLOR_RGB2BGR)
            
            # VideoWriterにフレームを追加
            out.write(img)
            
        else: # キャプチャー画像がない場合はループ終了 
            break
    
    # 再生画像をクローズ
    cap.release() 

# 出力動画ファイルをクローズ
out.release() 

# gif動画保存
images[0].save('subtitles.gif', save_all=True, append_images=images[1:], 
               optimize=True, duration=40, loop=0)       
実行結果

 3つの動画が字幕を付けて1つの動画にまとめられました。
f:id:T_A_T:20220206173827g:plain

以下のサイトを参考にさせていただきました

Pythonの文法メモ > 【OpenCV】動画ファイルの読み出しとプロパティ取得、キャプチャー画像の保存
Pythonの文法メモ > 【Pillow】アルファチャンネル付き図形と画像の合成

ブログランキングに参加しています

にほんブログ村 IT技術ブログへ
にほんブログ村