乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

プログラミング

今をときめくスーパースター
江頭2:50様と

今年引退するとかしないとか
よく分からないと噂のarashiメンバーの顔を
入れ替えて遊んでみました。



解説動画はこちら


前回やった
opencvのプログラムで
また顔の入れ替えをしてみます。


メンバー5人の

目鼻口を

江頭2:50さんの顔に当て込んでみます。



さてどうなるでしょうか!!!

・・・


・・





どーーーん
matuj

5人とも
ほぼ江頭さんになりますが
顔の特徴が微妙に出ているので
なかなか興味深いですね!!!!

5人結果の詳細は
動画をご覧くださいませ。

それでは

昔どっかの番組で
松崎しげるさんがどんどん黒くなったらいつ気づくのか?
みたいな番組をやっていたのを思い出しました。

今回はopencvを使って
画像の一部分の黒さを濃くするプログラムを作ってみました。

解説動画はこちら



ソースコードはこちら

画像はご自身で用意してくださいね。

import cv2
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, IntSlider
%matplotlib inline

def detect_red_color(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    hsv_min = np.array([0,64,0])
    hsv_max = np.array([30,255,255])
    mask1 = cv2.inRange(hsv, hsv_min, hsv_max)
    hsv_min = np.array([150,64,0])
    hsv_max = np.array([179,255,255])
    mask2 = cv2.inRange(hsv, hsv_min, hsv_max)    
    mask = mask1 + mask2
    masked_img = cv2.bitwise_and(img, img, mask=mask)
    return masked_img

img = cv2.imread(‘ファイルパス’)
red_masked_img = detect_red_color(img)
mask_img = cv2.cvtColor(red_masked_img, cv2.COLOR_BGR2RGB)

rate1 = IntSlider(min=1 , max=20,step=1,value=20)

@interact(rate1=rate1)
def plot(rate1):
    mask = mask_img  // 20 * rate1 
    mask[mask==0]=255
    height, width, color = img.shape
    for y in range(0, height):
        for x in range(0, width):
            if (mask[y][x] < 240).all():
                img[y][x] = mask[y][x]
    plt.figure(figsize=(16,9))
    plt.imshow(img)
    plt.show()

ファイルパスの部分だけ
ご自身で用意したファイルに差し替えれば
動くと思います。

このプログラムですが
画像の色味を見てその濃さを調整するので
できれば顔以外は真っ黒な方が望ましいです。

スライダーで黒みを調整できます。
初期値は20で
この時は0%です。

スライドを左にずらして行けば
5%ずつ濃くなっていきます。

MAXにしてみると
download-1

ぶっちゃけ誰か分かりません!!!!

プログラム内で何をやっているかと言うと
まず赤みを判定して
顔のマスク部分を作る関数があります。

赤みの部分だけ抽出し
その濃さをスライダーの値から
計算して黒に近づけます。

画像は取り込んだ際に
配列で取り込まれているので
データ上は数値です。

RGB値だと
黒は0,0,0
白は255,255,255です。

単純に数値を
割って小さくすれば
黒に近づきます。

最後にマスク部分だけ
元の画像の色と入れ替えて表示
と言うことだけやっている
シンプルなプログラムです。

他の画像でも
色味を変えることはできますので
色々な方々でやってみてください。

本日はこれまで
それでは

在宅勤務他で暇なので
ドット絵クイズを作ってみました。

問題の動画はこちら



ドット絵を作る
ソースコードはこちら
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
%matplotlib inline

def resize1(img):
    height, width = img.shape[:2]
    rate = 720 / height
    n_width = round(width * rate)
    size = (n_width,720)
    return cv2.resize(img, size)

def resize2(img):
    height, width = img.shape[:2]
    rate = 256 / height
    n_width = round(width * rate)
    size = (n_width,256)
    return cv2.resize(img, size)

def center_img(img):
    img2 = np.zeros((720, 1280, 3), np.uint8)
    height, width = img.shape[:2]
    img2[0:height, (1280-width)//2:(1280-width)//2+width] = img
    return img2

def sub_color(src, K):
    Z = src.reshape((-1, 3))
    Z = np.float32(Z)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    ret, label, center = cv2.kmeans(
        Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    center = np.uint8(center)
    res = center[label.flatten()]
    return res.reshape((src.shape))

def anime_filter(img, K):
    gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
    edge = cv2.blur(gray, (3, 3))
    edge = cv2.Canny(edge, 50, 150, apertureSize=3)
    edge = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)
    img = sub_color(img, K)
    return cv2.subtract(img, edge)

def mosaic(src, ratio=0.25):
    small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
    return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)

input_path = 'フォルダのパス'
file_name = 'ファイル名'
input_file_path = input_path + file_name

# ファイル読み込み
img = cv2.imread(input_file_path, cv2.IMREAD_COLOR)
# リサイズ
img = resize1(img)
# アニメ化
img = anime_filter(img, 16)
# モザイク処理(img,粒度)
img = mosaic(img,0.01)
# リサイズ2
img = resize2(img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
fig, ax = plt.subplots(figsize=(16,9))
ax.imshow(img)
plt.show()



download

やっていることは
画像を取り込んで
リサイズ加工し
ドット絵加工しています。

モザイクの粒度を変えれば、
もう少し判別できる形になります。
小さくすればするほど荒く
大きくすると細かくなります。

ぜひ挑戦してみてください。
分かった秒数を
コメント欄に是非!!!

それでは

新型コロナウイルスが流行ってきています。

ありがたいことに
データをまとめて頂いている方が
いらっしゃるので
そのデータを用いて可視化をしてみます。


解説動画はこちら




データの提供元は東洋経済新聞社の方のようです。
めちゃくちゃ感謝したいと思います。


新型コロナウイルス国内感染の状況
https://toyokeizai.net/sp/visual/tko/covid19/

制作:荻原 和樹(東洋経済オンライン編集部)
データソース:厚生労働省の報道発表資料。

更新履歴・ソースコード:GitHub
https://github.com/kaz-ogiwara/covid19/

さてそれではデータを可視化をしていきましょう。

まずは
ライブラリの読み込みです。

今回は下記のライブラリを用います。
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline


次に
データの読み込みです。
直接データを読み込み指定します。

file_path = 'https://toyokeizai.net/sp/visual/tko/covid19/csv/data.csv'
df = pd.read_csv(file_path)
df.head()


新No.
旧No.確定日年代性別居住地周囲の患者の発生※濃厚接触者\nの状況
0111/1530代神奈川県なし38名特定、健康観察終了
1221/2440代中国(武漢市)なし32名特定、健康観察終了
2331/2530代中国(武漢市)なし7名特定、健康観察終了
3441/2640代中国(武漢市)No.192名特定、健康観察終了
4551/2840代中国(武漢市)なし3名特定、健康観察終了

こんな感じのローデータですね。

年代や性別などで
日別の感染者の状況をまとめてくれています。


はじめに
性年代別で集計してみましょう。

年代の並びがアレなので先に指定しておきます。
# 年代の並びを指定
order = ['10歳未満', '10代', '20代','30代','40代','50代','60代','70代','80代','90代']
data = pd.crosstab(df['性別'],df['年代'])[order]
data
年代10歳未満10代20代30代40代50代60代70代80代90代
性別
01941112151231
4161017283318120

これをヒートマップにしてみましょう。
ヒートマップはseabornライブラリで可視化できます。
data = pd.crosstab( df['性別'],df['年代'])[order]
ax = sns.heatmap(data,annot=True, fmt="d")
ax.set_ylim(len(data), 0)
plt.title('性年代別感染者数')
plt.show()
download-4

棒グラフにもしてみます。
棒グラフはseabornでは
countplotで可視化できます。
sns.countplot(y="年代", hue="性別",data=df,order=order)
plt.title('性年代別感染者数')
plt.show()

download-3
50-60代の男性の感染者が多いですね。

全体的には女性に比べると
男性の方が感染する方が多いようです。

男性の方がかかりやすいのかどうかは
このデータからは分かりませんね。



次に日別です。

日の並びも先に指定します。
date_order = ['1/15', '1/24', '1/25', '1/26', '1/28', '1/29', '1/30', '1/31','2/4', '2/5', '2/11',
       '2/13', '2/14', '2/15', '2/16', '2/17', '2/18', '2/19', '2/20', '2/21',
       '2/22', '2/23', '2/24', '2/25', '2/26', '2/27', '2/28', '調査中']
plt.figure(figsize=(16,9))
sns.countplot(x="確定日", hue="性別",data=df,order=date_order)
plt.title('日別感染者数')
plt.show()
download-2

日別の性別での可視化です。

男女を積み上げ棒グラフにしてみます。
data = pd.crosstab(df['性別'],df['確定日'])[date_order].T

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(1, 1, 1)
ax.bar(date_order, data['男'], label='男')
ax.bar(date_order, data['女'], bottom=data['男'], label='女')
ax.legend()
plt.title('日別感染者数')
plt.show()
download-1
2/22,2/27に感染者数が激増していますね。

グラフを累積にしてみます。
累積のデータを先に作って
それを可視化しています。

numpyのcumsumで累積データを作ることができます。
data = pd.crosstab(df['性別'],df['確定日'])[date_order]
data2 = np.cumsum(data.T)

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(1, 1, 1)
ax.bar(date_order, data2['男'], label='男')
ax.bar(date_order, data2['女'], bottom=data2['男'], label='女')
ax.legend()
plt.title('日別累積感染者数')
plt.show()
download


最後に都道府県別でみてみましょう。
data = pd.crosstab(df['居住地'],df['性別'])
data2 = data.sort_values('男',ascending=True)
data2


fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(1, 1, 1)
ax.barh(data2.index, data2['男'], label='男')
ax.barh(data2.index, data2['女'], left=data2['男'], label='女')
ax.legend()
plt.title('都道府県別感染者数')
plt.show()
download-5

北海道がすごいことになっていますね。

2番目に東京と愛知です。

地域差はかなりあると思いますが
そもそも目には見えないので
どこに潜んでいるかは
正直感じることもできないと思います。

企業の中には
在宅勤務を始める会社も出てきていて

人混みの中に出ないことが
感染予防につながると思いますので
リモートワークはどんどん広がっていくと
いーなーと思っています。

まあ
我々エンジニアは
客先に行くことなんて稀なので
全然在宅でも仕事できちゃいますよね。

2,3月は
大人しくしていた方が
良さそうな気はしますね。

この機会に
Pythonプログラミングを
覚えてみてはいかがでしょうか?

Youtubeなどでも
講座を公開していますので
ぜひみてみてください。

それでは。

さて久々の画像加工の回です

opencvを用いると簡単に画像の加工ができます。



解説動画はこちら




はいそれではまず最初に
opencvを使うにはライブラリを読み込みましょう。

インストールしていない方は
pip などでインストールしてみてください。

まず最初に画像を加工する関数を作ります。

アニメ画像みたいな加工をする関数を作ります。
def anime_filter(img, K= 20):
    gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
    edge = cv2.blur(gray, (13, 13))
    edge = cv2.Canny(edge, 10, 40, apertureSize=3) 
    edge = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)
    img = np.array(img/K, dtype=np.uint8)
    img = np.array(img*K, dtype=np.uint8)
    return cv2.subtract(img, edge)
これで画像をアニメ風にします。

やっていることはエッジの抽出とぼかしです

次にモザイク処理する関数を作ります。

def mosaic(src,ratio=0.07):
    img = src.copy()
    x, y, w, h = 0, src.shape[0]//3,src.shape[1],src.shape[0]//3
    tmp = img[y:y + h, x:x + w]
    small = cv2.resize(tmp, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
    img[y:y + h, x:x + w] = cv2.resize(small, tmp.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)
    return img

モザイクはopencvのresize機能で実現できます。
この関数で画像の一部分にだけ
モザイク処理をすることができます。

はい
それでは実際に画像に処理してみましょう。

画像は4枚使用しますが
画像はご自身で用意して下さいね。

img1_path = 'smap/sawaziri.png'
img2_path = 'smap/makky.png'
img3_path = 'smap/ASKA.png'
img4_path = 'smap/pierre.png'
paths = [img1_path,img2_path,img3_path,img4_path]

fig = plt.figure(figsize=(20,16))

for i,(path,A) in enumerate(zip(paths,['S','M','A','P'])):
    ax = fig.add_subplot(1, 4, i+1)
    img = cv2.imread(path)
    anime = anime_filter(img)
    mos = mosaic(anime)
    cv2.putText(mos, A, (mos.shape[1]//2, mos.shape[0]//4), 
                cv2.FONT_HERSHEY_PLAIN, 4, (255, 255, 255), 5, cv2.LINE_AA)
    ax.imshow(cv2.cvtColor(mos, cv2.COLOR_BGR2RGB))
    ax.set_xticks([])
    ax.set_yticks([])
plt.show()

実行した結果は
・・・・・


download


はいこのようになりました。
アニメっぽくして
モザイクをかけて
文字を追加しております!!!!

S
M
A
P
で始まる方々を
画像加工してみました。

はい
ということで
opencvを用いると
簡単に画像加工をすることができるので
皆さんも試してみて下さいね

今回はここまでです
それでは


このページのトップヘ