ITいろいろ

プログラミングやクラウド、データ解析など、ITにかかわることをいろいろと書いています。

Pandasを使ってExcelデータを取り込み&データベース化

データ分析をする時にもPythonは便利です。

Excelでできるようにフィルタ、ソートなどの整形、グラフ化だけでなく、機械学習などの応用もできますし、Excelで取り扱えない100万行以上のビッグデータの解析にも活用できます。

使用するデータ

総務省統計局が出している人口推計のデータを使用します。

2022年6月のデータは以下からダウンロードできます。

人口推計 各月1日現在人口 月次 2022年6月 | ファイル | 統計データを探す | 政府統計の総合窓口

ソースコード紹介

# パッケージのインストール(初回のみ)
!pip install pandas     # データの取り込み、整形などができるパッケージ
!pip install openpyxl   # Excelの取り込みに必要

# インポート
import pandas as pd

# ファイルの取り込み
# skiprows: 初めの行を指定された数だけ取り除く
# skipfooter: 最後の行を指定された数だけ取り除く
# header: 列名とする行番号。デフォルトが0なので省略可。列名がない場合はNoneにする
# usecols: 取り込む列A列からH列とI列を取り込む。デフォルトでは自動ですべての列を取得するため、こちらも省略可。
df = pd.read_excel("05k2-3.xlsx", skiprows=3, skipfooter=3, header=0, sheet_name="05k2-3", usecols="A:H,I")

# ファイルの書き出し
df.to_excel('result.xlsx')

# 結果の表示
df

結果

コンソール

result.xlsx

少し解説

read_excel()

この人口推計のデータ、少しクセがあります。

はじめ3行に表の説明が書かれています。

最後の3行も注意書きがあります。

こういったデータは分析するときに邪魔になります。

read_excel関数の引数をうまく使えば、たった1行で取り込む際にこういった余計なデータを削除して分析用のデータに整形することができます。

それぞれの引数はコードのコメントアウトに書いていますので、そちらを参照ください。

read_csv()

read_excelの代わりにread_csvを使うと、CSVデータの取り込みも可能です。

ただ、python usecols="A:H,I"のようにアルファベットの列番号を使うことができないため、python usecols=[0,1,2,3,4]のように数字の列番号を使用します。(範囲指定ができないのが少しネック)

色調で簡易的な物体検出

今日は色調を見て赤色のものを検出するプログラムを紹介します。 色で検知するにはHSVを利用します。こちらもまた後程紹介します。

コード紹介

# import
import cv2

# 検知する範囲の指定
target_x1, target_x2, target_y1, target_y2 = 300, 350, 200, 250

# Webカメラから入力を開始
cap = cv2.VideoCapture(0)
while True:
    # カメラの画像を読み込む
    _, frame = cap.read()

    # 判定個所のトリミング
    frame_judge = frame[target_y1:target_y2, target_x1:target_x2]

    # HSV変換
    hsv = cv2.cvtColor(frame_judge, cv2.COLOR_BGR2HSV)

    # HSVそれぞれを一次元配列に格納
    h, s, v = hsv[:, :, 0].flatten(), hsv[:, :, 1].flatten(), hsv[:, :, 2].flatten()
    
    # HSVそれぞれの平均
    h_mean, s_mean, v_mean = h.mean(), s.mean(), v.mean()

    # 検知範囲を矩形で表示
    cv2.rectangle(frame, (target_x1, target_y1), (target_x2, target_y2), (255, 255, 255), thickness=2)

    # print(h_mean, s_mean, v_mean)
    if( ( 0 <= h_mean < 30 or 150 <= h_mean < 180 ) and s_mean >= 100 ):
        # 文字の出力
        cv2.putText(frame, text='Red!', org=(50, 50), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1.0, color=(0, 0, 255), thickness=2)

    # ウィンドウに画像を出力
    cv2.imshow('Camera', frame)
    
    # ESCかEnterキーが押されたらループを抜ける
    k = cv2.waitKey(500) & 0xFF  # 500msec毎
    if k == 27 or k == 13: break

cap.release()   # カメラを開放
cv2.destroyAllWindows() # ウィンドウを破棄

出力結果

枠内に赤色が入った時に「Red!」と表示されるようになりました。

少し解説

HSVについて

色の表現を以下3つの観点で行う方法です。

H: 色相
S: 彩度
V: 明度

引用:Wikipedia ja.wikipedia.org

Wikipediaの画像にあるように、Sの値が小さいと黒っぽいもしくは白っぽい色になるため、Sは100以上にします。

Hの値は以下の表を参考にしてください。

Hの値
0~30, 150~179
30~90
90~150

関数

flatten() 多次元配列を一次元配列に変換

python hsv[:, :, 1]は[[1,1,...,1][1,1,...,1]]のように多次元になっています。python hsv[:, :, 1].flatten()とすることで、[1,1,...,1]というように一次元の配列にすることができます。

mean() 平均値の計算

上記一次元配列にしたもの(h)に対して、python h.mean()とすることで配列内の平均値を求めることができます。

つまり、検知範囲の中のH(色相)の平均を出していることになります。

cv2.rectangle 画像に四角形を表示

python cv2.rectangle(frame, (target_x1, target_y1), (target_x2, target_y2), (255, 255, 255), thickness=2) で、四角形を表示しています。

第一引数:画像
第二引数:左上の座標
第三引数:右下の座標
第四引数:線の色
第五引数:線の太さ

となります。

cv2.putText 画像にテキストを表示

python cv2.putText(frame, text='Red!', org=(50, 50), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1.0, color=(0, 0, 255), thickness=2)でテキストを表示します。

第一引数:画像
第二引数:表示するテキスト
第三引数:左上の座標
第四引数:フォント種類
第五引数:フォントサイズ
第六引数:テキストの色
第七引数:テキストの太さ

です。

OpenCVで動画を扱う

OpenCVは動画の扱いも比較的楽に行えます。

少しずつ動画処理の記事も増やしていこうと思います。

コード紹介

# import
import cv2
import time

# 動画データを取り込む
# 0以上の数字:Webカメラを入力とする(どの数字かはPC環境による)
# './sample.mp4'など動画ファイルを指定:動画ファイルを入力とする
cap = cv2.VideoCapture(0)

# たまにエラーになるため、表示前に3秒待つ
time.sleep(3)

while True:
    # カメラの画像を読み込む
    _, frame = cap.read()

    # 青色要素を0にする
    # frame[:,:,0] = 0    

    # ウィンドウに画像を出力
    cv2.imshow('Camera', frame)
    
    # ESCかEnterキーが押されたらループを抜ける
    k = cv2.waitKey(100) & 0xFF  # 100msec確認
    if k == 27 or k == 13: break

cap.release()   # カメラを開放
cv2.destroyAllWindows() # ウィンドウを破棄

出力結果

少し解説

cap = cv2.VideoCapture(0)で動画を取得する準備をします。 中の0は、PCによって変わります。 (私のSurfaceは0がアウトカメラ、1がインカメです。USBでWebカメラを接続すると、おそらく2で取得できると思います)

while True:は無限ループの意味です。

_, frame = cap.read()で動画から画像ファイルとしてキャプチャし、frameに格納します。 1つ目は読み込めたかどうか、True,Falseで返すものですが、今回は使わないので「_」としています。

cv2.waitKey(100) & 0xFFは100msec(0.1秒)キーボードの入力を待つ、ということで、何も入力しなければ次に行く、つまりループに戻ってまた動画をキャプチャして・・・、という繰り返しです。

少し改造

解説で説明したように、結局は動画も1枚1枚の画像にしています。

そのため、今までの画像処理がそのまま使えます。

例えばpython frame[:, :, 0] = 0を追加してみましょう。(コード紹介のコメントアウトを外します。

すると青の要素がなくなった画像が、動画として流れます。

画像の配列をいじって色調を変える

OpenCVcv2.imreadで画像を取り込むと、画像1ピクセルごとの色情報が配列で読み取られます。

今回はそれを活用して、色調を変えてみます。

画像の配列について

まずOpenCVで読み取った画像をそのまま出力してみます。

# インポート
import cv2

# 画像ファイルの読み込み
img = cv2.imread("Lenna.jpg")

# 表示
img

すると以下のように3次元の配列で表示されます。

少しわかりずらいので、縦2ピクセル、横3ピクセルの画像で説明すると、こんな配列になります。

[
    [
        [青, 緑, 赤], [青, 緑, 赤], [青, 緑, 赤]
    ],
    [
        [青, 緑, 赤], [青, 緑, 赤], [青, 緑, 赤]
    ]
]

今までも何度か出てきていますが、OpenCVBGRの順番です。間違えやすいので注意。

赤要素をなくす(0にする)

コード紹介

# インポート
import cv2
import matplotlib.pyplot as plt

# 画像ファイルの読み込み
img = cv2.imread("Lenna.jpg")

# サイズを取得
height,width,color = img.shape

# 赤要素を0にする
img[:, :, 2] = 0

# 配列を表示
print(img)
# 画像を出力
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))

出力結果

少し解説

img[:, :, 2] = 0で、3次元目の要素2をすべて0にするということになります。 0→青、1→緑、2→赤なので、赤要素を0にするということですね。 1次元目と2次元目の「 : 」は、すべて、という意味になります。

応用

縦0~100ピクセルだけ変える

img[0:100, :, 2] = 0

縦0~100ピクセル、横100~300ピクセルだけ変える

img[0:100, 100:300, 2] = 0

青(0)と赤(2)両方0にする(つまり緑要素だけを取り出す)

img[0:100, 100:300, (0, 2)] = 0

OpenCVでの画像のリサイズとpyplotで複数画像を表示

今回はOpenCVで画像をリサイズしてみます。 そして、今まではOpenCVの画像出力cv2.imshowを使っていたのですが、pyplotを使って複数の画像を一度に表示する方法も記述します。

コード紹介

# matplotlibのインストール(初回のみ)
!pip install matplotlib

# インポート
import cv2
import matplotlib.pyplot as plt

# 画像ファイルの読み込み
img = cv2.imread("Lenna.jpg")

# サイズを取得
height,width,color = img.shape

# リサイズ(指定した値でリサイズ)
img_resize1 = cv2.resize(img, dsize=(300, 100)) #dsize=は省略可

# リサイズ(倍率でリサイズ)
img_resize2 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5)


# 左右3分割で左側に元画像を表示
plt.subplot(1, 3, 1)
plt.imshow(cv2.cvtColor(img_resize1,cv2.COLOR_BGR2RGB))

# 2番目にimg_resize1を表示
plt.subplot(1, 3, 2)
plt.imshow(cv2.cvtColor(img_resize2,cv2.COLOR_BGR2RGB))

# 3番目にimg_resize2を表示
plt.subplot(1, 3, 3)
plt.imshow(cv2.cvtColor(img_resize2,cv2.COLOR_BGR2RGB))

出力結果

少し解説

リサイズcv2.resizeについて

あまり説明することはないですが、dsizeパラメータは値の指定、fx, fyは倍率を指定できます。

dsizeは必須ですので、倍率で指定するときはdsize=Noneを忘れないようにつけましょう。

pyplotについて

plt.imshow関数で画像を表示することができます。

imshowの前にplt.subplotを設定することで複数画像を表示することができます。

plt.subplot(1, 3, 2)とすると、縦1行、横3列の枠の中で2番目に表示する、という意味になります。

注意点:OpenCVはBGR、PyplotはRGB

画像を表示するときに python plt.imshow(img) とやりたくなるのですが、そうするとレナさんの顔色が悪くなります。。

これは扱う配列の順番がOpenCVは[青、緑、赤]に対し、pyplotは[赤、緑、青]になるからです。

このため、コード紹介にあるように変換をしてから表示するようにする必要があります。

OpenCV/ガウシアンフィルタで画像をぼかす

今日はぼかしについて記述していきます。

コード紹介

# インポート
import cv2

# 画像ファイルの読み込み
img = cv2.imread("Lenna.jpg")

# ガウシアンフィルタでぼかす
img_blur = cv2.GaussianBlur(img, (9,7), 0)

# 読み込んだ画像の出力。
cv2.imshow('Lenna_sobel', img_blur)

# 何かのキーを入力待ちする。64bitのマシンの場合は「& 0xFF」が必要らしい
cv2.waitKey(0) & 0xFF
# ウィンドウを閉じる
cv2.destroyAllWindows()

出力結果

ちょっとだけぼけた。

引数の解説、変更

cv2.GaussianBlur(img, (9,7), 0)の第二引数、(9,7)はksize(カーネルサイズ)と呼ばれるもので、画像の中で横9ピクセル、縦7ピクセルを取り、その箱の中で各ピクセルの色を何にするのかを計算する形になっています。なのでこの箱を大きくすると、もう少し荒い(ぼけた)画像になります。

第三引数の0は、細かいことは分かりませんが、ぼけ度と思ってもらったらよいです。0は自動、1以上は大きくなるほどぼけ度が上がります。

img_blur = cv2.GaussianBlur(img, (15,13), 0)

img_blur = cv2.GaussianBlur(img, (15,13), 10)

終わりに

ぼかす方法はこの他にも平均値フィルタcv2.blur、中央値フィルタcv2.medianBlurなどありますが、ガウシアンフィルタがシンプルなコードでかつぼけ度を変えられるので、おすすめです。

OpenCVのエッジ検出の方法をいくつか紹介

今回はエッジ検出です。Pillowもエッジ検出の方法はあるようですが、いったんOpenCVを使った方法をまとめたいと思います。

エッジ検出の方法

今回は3つ紹介します。

  • Soble(ソーベル)法
  • Laplacian(ラプラシアン)法
  • Canny(キャニー)法

詳しいことはまだ無知なため割愛。。 使ってみてのおすすめはCanny(キャニー)法です。

Cannyを使ったエッジ検出

コード紹介

# インポート
import cv2

# 画像ファイルの読み込み
img = cv2.imread("Lenna.jpg")

# Cannyを使ったエッジ検出
img_canny = cv2.Canny(img, 100, 100)

# 読み込んだ画像の出力。
cv2.imshow('Lenna_canny', img_canny)

# 何かのキーを入力待ちする。64bitのマシンの場合は「& 0xFF」が必要らしい
cv2.waitKey(0) & 0xFF
# ウィンドウを閉じる
cv2.destroyAllWindows()

出力結果

少し解説

cv2.Cannyという関数を使います、第一引数には変換したい画像、第二引数と第三引数には閾値を入れます。 ここも詳細は分からないのですが、とりあえず両方100にしています。

その他のエッジ検出

ラプラシアン

# Laplacianを使ったエッジ検出
img_laplacian = cv2.Laplacian(img, cv2.CV_32F, 3)

ソーベル

# Sobelを使ったエッジ検出
img_sobel = cv2.Sobel(img, cv2.CV_32F, 1, 1, 3)

あまりエッジ検出、という感じの結果にはなりませんでした。 もう少し引数を変えるとうまくいくのかもしれません。 分かり次第またアップデートします。