Kazuhito00のプログラミング、その他、備忘録。

日々調べてたことや、作ってみたものをメモしているブログ。 お決まりの断り文句ですが、このブログに書かれている内容は個人の見解であり、所属する組織の公式見解ではありません。チラ裏。

【OpenCV】【Python】画像上に日本語のテキストを描画

もはや、タイトルが詐欺くさい気がしないでもないです。
OpenCV単体だと大変なのでPillow(PIL)を利用しております。

日本語テキストを描画しようとしたら、
パッと思いつく範囲で以下のような対応があります(大変な順に。。。
 ・OpenCVソースコードを修正しputTextをマルチバイト文字対応させる
 ・日本語フォントの文字画像を用意し、対象画像に重ね合わせて描画する
 ・日本語対応している他ライブラリに渡して描画する

今回はパッと表示したいだけだったので、Pillowを利用します。
ソースコードは以下。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

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

class CvPutJaText:
    def __init__(self):
        pass

    @classmethod
    def puttext(cls, cv_image, text, point, font_path, font_size, color=(0,0,0)):
        font = ImageFont.truetype(font_path, font_size)
        
        cv_rgb_image = cv.cvtColor(cv_image, cv.COLOR_BGR2RGB)
        pil_image = Image.fromarray(cv_rgb_image)
        
        draw = ImageDraw.Draw(pil_image)
        draw.text(point, text, fill=color, font=font)
        
        cv_rgb_result_image = np.asarray(pil_image)
        cv_bgr_result_image = cv.cvtColor(cv_rgb_result_image, cv.COLOR_RGB2BGR)

        return cv_bgr_result_image

使用例は以下。

今回はフォントに「衡山毛筆フォント行書」を使用しております。

if __name__ == '__main__':
    cv_image = cv.imread("sample.jpg")
    
    font_path = './font/font_jb004_running_brush_wi.ttf'
    
    image = CvPutJaText.puttext(cv_image, u"ごんべ", (30, 30), font_path, 60, (0, 0, 0))

    cv.imshow("sample", image)
    cv.waitKey(0)

f:id:Kazuhito00:20180620025707p:plain 以上。

【Keras】メモ:Kerasのモデルを別スレッドで使用する

使用したいときに忘れているためメモ。

マルチスレッドでKerasのモデルを利用したい時には、 ロードしたモデルに対して、以下のAPIを呼んで事前コンパイルしておく。 (メイン関数でロードしたモデルを別スレッドに渡して、別スレッド内で推論する場合等)

model._make_predict_function()

以上。

【LINE】個人的に色々作ったBOT

これまた今さらですが、LINE Messaging APIを利用したLINE BOTの作成が楽しく、色々試しております。

以下に最近作ってみたものをご紹介。

 

■献立提案 BOT

 献立を考える助けになるかもしれないBOT

 ・使用技術

  - 楽天レシピAPI

  - ホットペッパー グルメサーチAPI

  - Google Apps Script

 ・機能

  - 食材からレシピを提案(写真1枚目)

  - 位置情報から最寄りのお店を提案(写真2枚目)

  - 特定のスタンプを送るとランダムでレシピを提案(写真3枚目)

f:id:Kazuhito00:20180318025837p:plain
f:id:Kazuhito00:20180318025956p:plain
f:id:Kazuhito00:20180318025958p:plain

 

OCR BOT

  画像を送るとOCR結果を返すBOT

 ・使用技術

  - Google Apps Script

  - Google Cloud Vision API(光学式文字認識(OCR))

 ・機能

  - 送付された画像のOCR結果を返す

  - 画像とOCR結果をスプレッドシートに更新してGoogleドライブ上に保存

f:id:Kazuhito00:20180318025959p:plain

 

■画像判別 BOT

  画像を送ると画像判別結果を返すBOT

 ・使用技術

  - Google Apps Script

  - Google Cloud Vision API(ラベル検出)

  - Microsoft Azure TranslatorAPI

 ・機能

  - 送付された画像に対し、ラベル検出結果を返す

    (ラベルは英語なので、日本語に翻訳した結果を返す)

f:id:Kazuhito00:20180318030001p:plain

 

以上。

【GAS】Googleドライブ上のスプレッドシートを追記するWeb API

今さらながらGASの便利さに気付き色々試しております。

本頁は、Google Apps Script をWebAPIとして利用して、Googleドライブ上のスプレッドシートに追記するメモです。


Googleドライブ上にスプレッドシートを用意します。 f:id:Kazuhito00:20180318023626j:plain

GASからアクセスできるように共有可能なリンクを取得します。 f:id:Kazuhito00:20180318023637j:plain

「?id=」以降がGAS側で利用するIDです。 f:id:Kazuhito00:20180318023651j:plain

たいしたコードでも無いので、ソースコードにそのままIDを埋め込んでも良いですが、 一応GASのプロジェクトプロパティにIDを入れておきます。 f:id:Kazuhito00:20180318023708j:plain f:id:Kazuhito00:20180318023716j:plain

GAS上のソースコードは以下です。 リクエストパラメータは手抜きしてactionをそのまま使用。。。

function doGet(e) {
  // action未指定の場合
  if(!e.parameter.action) { 
    return ContentService.createTextOutput(JSON.stringify({'status': 'NG'}))
           .setMimeType(ContentService.MimeType.JSON);
  }

  // スプレッドシートに記録
  // プロジェクトプロパティからスプレッドシートのIDを取得
  var PROPERTIES = PropertiesService.getScriptProperties();
  var SPREAD_SHEET = PROPERTIES.getProperty('SPREAD_SHEET')
  var sheet = SpreadsheetApp.openById(SPREAD_SHEET);
  // スプレッドシートに追記するCSVデータを生成
  var appendData = new Array();
  var cells = e.parameter.action.split(",")
  for(var i = 0; i<cells.length;i++){
    appendData.push(cells[i])
  }
  // スプレッドシートの最終行に追記
  sheet.appendRow(appendData);

  return ContentService.createTextOutput(JSON.stringify({'status': 'OK'}))
         .setMimeType(ContentService.MimeType.JSON);
}

ウェブアプリケーションとして導入」を実施します。 f:id:Kazuhito00:20180318023731j:plain

アプリケーションにアクセスできるユーザーは「全員(匿名ユーザーを含む)」に設定し「更新」を実施します。

初回はGoogleドライブ上へのアクセス許可を求められるはず。。 。 スクリーンショット取り忘れました。。。 f:id:Kazuhito00:20180318023743j:plain

テストとしてブラウザから「1,2,3, ,abcde」を指定して呼び出してみます。 f:id:Kazuhito00:20180318023754j:plain

スプレッドシートに追記されました。 f:id:Kazuhito00:20180318023808j:plain

以上。

【Python】OpenCVで疑似カラー(Pseudo color)

グレースケール画像に疑似的な色を付けることを疑似カラーと言います。
以下のようなデータがあった場合に、視覚的に分かりやすい表現が出来ます
 ・深度センサー(KinectやRealSenseやXtion等のDepthカメラ)
 ・サーモグラフィー
 ・照明ムラ
  など

OpenCVに疑似カラーを付けるAPI(applyColorMap())があったため、本投稿にメモ。

動画は以下。
www.youtube.com

ソースコードは以下。
本来ならサーモグラフィー等の画像を取り込みたいところですが、
Webカメラしかなかったので、グレースケール画像を取り込んで疑似カラーを付けてます。
色々なパターンがあるため、N押下で切り替えれるようにサンプルを作成。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import cv2
import copy

colormap_table_count = 0
colormap_table = [
    ['COLORMAP_AUTUMN',  cv2.COLORMAP_AUTUMN ],
    ['COLORMAP_JET',     cv2.COLORMAP_JET    ],
    ['COLORMAP_WINTER',  cv2.COLORMAP_WINTER ],
    ['COLORMAP_RAINBOW', cv2.COLORMAP_RAINBOW],
    ['COLORMAP_OCEAN',   cv2.COLORMAP_OCEAN  ],
    ['COLORMAP_SUMMER',  cv2.COLORMAP_SUMMER ],
    ['COLORMAP_SPRING',  cv2.COLORMAP_SPRING ],
    ['COLORMAP_COOL',    cv2.COLORMAP_COOL   ],
    ['COLORMAP_HSV',     cv2.COLORMAP_HSV    ],
    ['COLORMAP_PINK',    cv2.COLORMAP_PINK   ],
    ['COLORMAP_HOT',     cv2.COLORMAP_HOT    ]
]

video_input = cv2.VideoCapture(0)

while(1):
    ret, frame = video_input.read()

    # グレースケール化
    grayscale_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 疑似カラーを付与
    apply_color_map_image = cv2.applyColorMap(grayscale_image, colormap_table[colormap_table_count % len(colormap_table)][1])

    cv2.putText(apply_color_map_image, colormap_table[colormap_table_count % len(colormap_table)][0], (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0,0,0),2);
    cv2.imshow('apply_color_map_image',apply_color_map_image)

    k = cv2.waitKey(50) & 0xff
    if k == 110: # N
        colormap_table_count = colormap_table_count + 1
    if k == 27:  # ESC
        break

video_input.release()
cv2.destroyAllWindows()

【Windows】【Python】OpenCV3.3.1のdnnモジュールサンプル(mobilenet_ssd_python.py)

OpenCV3.3で公式にサポートされたDNN(深層ニューラルネットワーク)モジュールの
Python版mobilenetサンプルを動作させてみました。

学習済みモデルは以下URLにあるはずだが、リンク切れていたため、
https://github.com/chuanqi305/MobileNet-SSD/blob/master/MobileNetSSD_train.caffemodel
ひとまず、以下のURLから取得。
https://drive.google.com/file/d/0BwY-lpO6tzxHRHNCdlRKczIzaEU/view?usp=sharing

動画は以下。
CPUのみでもそこそこ動く。
www.youtube.com

ソースコードは以下。
基本的には、「opencv\sources\samples\dnn\mobilenet_ssd_python.py」と同じ。
自分の環境構築が悪いのか、そのままで動作しなかったところを一部修正。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import argparse

try:
    #import cv2 as cv
    import cv2                #[修正] cv2呼び出ししているため、as cvを削除
    from cv2 import dnn       #[修正] dnnモジュールをインポート
except ImportError:
    raise ImportError('Can\'t find OpenCV Python module. If you\'ve built it from sources without installation, '
                      'configure environemnt variable PYTHONPATH to "opencv_build_dir/lib" directory (with "python3" subdirectory if required)')

inWidth = 300
inHeight = 300
WHRatio = inWidth / float(inHeight)
inScaleFactor = 0.007843
meanVal = 127.5

classNames = ('background',
              'aeroplane', 'bicycle', 'bird', 'boat',
              'bottle', 'bus', 'car', 'cat', 'chair',
              'cow', 'diningtable', 'dog', 'horse',
              'motorbike', 'person', 'pottedplant',
              'sheep', 'sofa', 'train', 'tvmonitor')

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--video", help="path to video file. If empty, camera's stream will be used")
    parser.add_argument("--prototxt", default="MobileNetSSD_300x300.prototxt",
                        help="path to caffe prototxt")
    parser.add_argument("-c", "--caffemodel", help="path to caffemodel file, download it here: "
                             "https://github.com/chuanqi305/MobileNet-SSD/blob/master/MobileNetSSD_train.caffemodel")
    parser.add_argument("--thr", default=0.2, help="confidence threshold to filter out weak detections")
    args = parser.parse_args()

    net = dnn.readNetFromCaffe(args.prototxt, args.caffemodel)

    #if len(args.video):      #[修正] --video未指定時にlen()で長さ取得できないはずなので修正
    if args.video != None:
        cap = cv2.VideoCapture(args.video)
    else:
        cap = cv2.VideoCapture(0)

    while True:
        # Capture frame-by-frame
        ret, frame = cap.read()
        blob = dnn.blobFromImage(frame, inScaleFactor, (inWidth, inHeight), meanVal)
        net.setInput(blob)
        detections = net.forward()

        cols = frame.shape[1]
        rows = frame.shape[0]

        if cols / float(rows) > WHRatio:
            cropSize = (int(rows * WHRatio), rows)
        else:
            cropSize = (cols, int(cols / WHRatio))

        y1 = (rows - cropSize[1]) / 2
        y2 = y1 + cropSize[1]
        x1 = (cols - cropSize[0]) / 2
        x2 = x1 + cropSize[0]
        frame = frame[y1:y2, x1:x2]

        cols = frame.shape[1]
        rows = frame.shape[0]

        for i in range(detections.shape[2]):
            confidence = detections[0, 0, i, 2]
            if confidence > args.thr:
                class_id = int(detections[0, 0, i, 1])

                xLeftBottom = int(detections[0, 0, i, 3] * cols)
                yLeftBottom = int(detections[0, 0, i, 4] * rows)
                xRightTop   = int(detections[0, 0, i, 5] * cols)
                yRightTop   = int(detections[0, 0, i, 6] * rows)

                cv2.rectangle(frame, (xLeftBottom, yLeftBottom), (xRightTop, yRightTop),
                              (0, 255, 0))
                label = classNames[class_id] + ": " + str(confidence)
                labelSize, baseLine = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)

                cv2.rectangle(frame, (xLeftBottom, yLeftBottom - labelSize[1]),
                                     (xLeftBottom + labelSize[0], yLeftBottom + baseLine),
                                     (255, 255, 255), cv2.FILLED)
                cv2.putText(frame, label, (xLeftBottom, yLeftBottom),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0))

        cv2.imshow("detections", frame)
        if cv2.waitKey(1) >= 0:
            break

ロジクール c922 購入 + OpenCV カメラ設定メモ

USB2.0Webカメラ界のエース c920rの後継機
https://www.amazon.co.jp/Logicool-%E3%83%AD%E3%82%B8%E3%82%AF%E3%83%BC%E3%83%AB-C922-%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%9F%E3%83%B3%E3%82%B0-%E3%82%A6%E3%82%A7%E3%83%96%E3%82%AB%E3%83%A0/dp/B01LYTKPDT

f:id:Kazuhito00:20161013014225j:plain

ぶっちゃけ、c920rとほぼほぼ性能に差はないが、
「720p(60fps)」と言う表記に惹かれ購入。
USB3.0系の産業用カメラならまだしも、市販機で60fpsを謳っているWebカメラは珍しい。

最近ほとんど使っていなかったOpenCVのカメラ設定周りのAPIを忘れないようにメモ↓

video_input.set(cv2.CAP_PROP_FPS, 60)           # カメラFPSを60FPSに設定
video_input.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) # カメラ画像の横幅を1280に設定
video_input.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # カメラ画像の縦幅を720に設定

print video_input.get(cv2.CAP_PROP_FPS)
print video_input.get(cv2.CAP_PROP_FRAME_WIDTH)
print video_input.get(cv2.CAP_PROP_FRAME_HEIGHT)