OpenCVで複数のテンプレートマッチングする方法

こんにちは。
くろんです!

今回はOpenCVを使って画像中から複数のテンプレートマッチングを行います。

今回はサンプル画像はこちら

かえるのうたの歌詞がのった画像を用意しました!!

こちらのサンプルから「ワ」を切り出した下のようなマッチング用画像を作りました、
サンプル画像から「ワ」をすべて抽出したいと思います。


#include <vector>
#include "opencv2/opencv.hpp"

int main()
{
	cv::Mat src = cv::imread("カエルの歌.png");
	cv::Mat key = cv::imread("カエルの歌KEY.png");

	cv::Mat res;
	cv::matchTemplate(src, key, res, cv::TM_CCOEFF_NORMED);

	std::vector<cv::Point> locs;

	for (int y = 0; y < res.rows; y++) {
		for (int x = 0; x < res.cols; x++) {
			uchar* uctemp = &amp;res.data[y * res.cols * 4 + x * 4];
			float* ftemp = (float*)(uctemp);
			if (*ftemp > 0.95) {
				bool isExist = false;
				for (auto ml : locs) {
					// 今までに見つかった場所からある程度離れないと検出扱いにしない.
					int d = pow(y - ml.y, 2) + pow(x - ml.x, 2);
					if (d < 10) {
						isExist = true;
					}
				}

				if (!isExist) {
					locs.push_back(cv::Point(x, y));
				}
			}
		}
	}

	for(auto l : locs) {
		cv::rectangle(src, cv::Rect(l.x, l.y, key.cols, key.rows),
										cv::Scalar(0, 0, 255));
	}

	cv::imshow("result", src);

	cv::waitKey();

	return 0;
}

結果はこんな感じになります。

しっかり「ワ」の場所だけ抽出できているかと思います。

以上

では!

文字コード変更プログラム公開しました

こんにちは。
くろんです!!

で思いがけず文字コードについて勉強する機会が出来たので、
なんとなく文字コードを変換するプログラムを作りました。

https://github.com/ckron/TextEncodeChanger/

こちらです、対応文字コードは

SHIFT_JIS: 932
UTF16_LE: 1200
UTF16_BE: 1201
UTF32_LE: 12000
UTF32_BE: 12001
EUC_JP: 20932
UTF7: 65000
UTF8: 65001

です。

内容は単純な文字コードの変換をするだけのものですが、文字コード周りで困っている方がいたらもしかしたら役に立つかもしれません。

役に立たないかなぁー?役に立ったらいいなー

使い方はmain.cpp にサンプル載せているのでそこら辺参照でお願いします。

では

itunesのエクスポートしたお気に入り情報読み込んで失敗した話

こんにちは。
くろんです!!

にてはまったポイントがあったので記録がてら記事にしておきます。

結論から言うと、完璧に解決はできずに応急処置的な感じで終わっていますので参考程度でどうぞ。

はまったポイントを簡潔に言うと

UTF-16LE(itunesお気に入りエクスポート文字コード)が正しく読み込めない

こんな感じです。

とりあえずやってみたことと失敗した内容は↓↓↓

ファイルからの読み込み

- stlを使う方法(ifstream/wifstream)
→ データをそのまま読み込めているようだが、1文字がchar型2バイトで読み込まれ、適切に1文字単位で読み込めない

文字コードの変換

- WideCharToMultiByte/MultiByteToWideChar
→ 文字コードをUTF-16LEにしても変換がうまくいかない(printf()で表示させるとバグる)

- std::wstring_convertを使う
→ C++17で非推奨 & 
変換しても表示がバグる

ここら辺はダメだった印象ですね…

ちなみにやってみてうまくいったのは、_wfopen_s() でした。

コードはこんな感じです。


setlocale(LC_CTYPE, "");

wchar_t	wstr[512];
FILE* pfile = nullptr;

_wfopen_s(&amp;pfile, L"./PATH", L"rt,ccs=UTF-16LE");
if(pfile == NULL) { return false; }

if(fgetws(wstr, 512, pfile) == NULL) {
  return;
}
fclose(pfile);

このコードだと一応、UTF-16LEのファイルを読み込めました!

setlocale()が効いているのかよくわかりませんが、setlocale()を外すとうまく読み込めないので大事なことなのかと。
詳しくはわからないので勉強しないとですね…

itunesからエクスポートしたお気に入り情報の読み込みで詰まったら、是非使ってみてください。

では!

GoogleHomeに自動でitunes曲をアップロードする別方法を考える(3)

こんにちは。
くろんです!

> の続きです。

久々ですね。
ここまでは必要項目のリストアップをしていました。

  1. itunesのプレイリスト情報を取得
  2. プレイリスト情報から曲の保存先情報を取得
  3. 曲を指定フォルダにコピーする
  4. wavをmp4に変換する
  5. Google Play Musicにアップロードする
  6. プレイリスト情報が変更されたときに1 ~ 5の処理をする

↑ の1 ~ 6ができれば自動化できるかな。と

このうち3 ~ 6 は楽にできそうだったのでざざっと考えているやり方書いて終わって、
1 ~ 2部分をいい感じにできる方法を考えます。

3. バッチ処理
4. ffmpegを使用し変換
5. 公式アプリケーションより自動的アップロード
6. バッチ処理

4 ~ 6はこんな感じですかね…

あとは1 ~ 2をどうするか

1 ~ 2の部分でやりたいことはitunesにある、任意のお気に入りにの曲情報を取得し、曲が保存されているパスを取得するということです。

色々探してみましたが、itunesのお気に入り情報が保存されている場所がどうしても探せなかったので、
とりあえず最初はお気に入りの情報をエクスポートし、そこからアップロードまでを自動化するようにしていきます。


名前	アーティスト	作曲者	アルバム	グループ	作品	楽章番号	楽章数	楽章名	ジャンル	サイズ	時間	ディスク番号	ディスク数	トラック番号	トラック数	年	変更日	追加日	ビットレート	サンプルレート	音量調整	種類	イコライザ	コメント	再生回数	最後に再生した日	スキップ回数	最後にスキップした日	マイ評価	場所

itunesのお気に入り情報は↑のようになってました。

1行目に各情報のタイトルが付いていて2行目以降はそれに対応する形でお気に入りに入っている曲情報が入っていました。
情報の区切りはタブを使っていました。

itunesのお気に入り情報で曲のパスは”場所”タイトルに入っているようなので、そこの箇所からパスを抜き出すようにします。

パスを抜き出すのは文字列操作すればいけるし、パスが抜き出せればあとはバッチファイルでもC++ならfilesystemでも使えばOKなのでとりあえずここまでで作れば出来上がりって感じですね!!

今回やっていた時に文字コード周りではまったポイントがあったので別記事にしておきます。

では

GASコード公開しましたー

こんにちは!
くろんです!!

適当に作ったGASコードをGitHubで公開したので、適当に乗せときます。https://github.com/ckron/ReceiptParser

GitHubのURLってこれでいいのかわからない?

今回は文字列が入力されたら、
いい感じにデータをパースして新しい行として挿入するプログラムです。

仮データ↓

Order ID B12AC
6/20/2018 12:00 AM
1x        Product_A        ¥2000
opt_A        ¥150             
opt_B        ¥50
1x        Product_B        ¥1500        
opt_A        ¥150
opt_C        ¥50
Sales        ¥3900
Promotion        (¥20)

こんな感じのデータが来たら、
IDと商品名と各オプション有無とプロモーション値を分けるものです。

仮データが来たら結果はこうなります。

B12AC 6/20/2018 Product_A 1 1 0 ¥20
B12AC 6/20/2018 Product_B 1 0 1 ¥20 

今回は最終結果までを作るのではなく中間のデータ抽出をする部分をGASで行いました。
全部をやろうとするのではなく、大変な作業を1か所コードで置き換えてあげると効率よく楽できます。

あとはマクロを使っていい感じにデータを集計します。

そんな大層なプログラムじゃないですが、GASを使うとこういうのできるよ!!
的なものとして公開しました。

GAS使うと色々効率化できますよ!!

では

gas使ってる

こんにちは!
くろんです!

突然ですが、ガスって知ってますか??

ガスはガスでも爆発しないガスです!
Google Apps Scriptってやつです!

端的に説明するとGoogle スプレッドシート/ドキュメント/プレゼンテーションとかで使えるスクリプトです。
(エクセルのVBA的な位置にいるやつです)

個人的にVBAよりいい感じなのでどこら辺が気に入っているか書いてきます。

1. 癖が少ない

基本的にJSがベースになっているので、gas独自の記述などはあまりありません。

なので、VBAみたいにそれ用の記述方法とかを頭に入れなくてもJSを触ったことがあれば割と感覚でコードが書ける印象です。

' 1: VBA
Sub hoge()
  ' ここにコード
End Sub
// 2: GAS
function hoge()
{
  // ここにコード
}

こんな感じでよくわからない Sub とかいう単語の意味を覚えなくても済みます。
(VBA以外で使わんし、覚えても無駄に思う)

少なくともGASならJSの知識も得られるので無駄にならない感じがいいですね。

2. ドキュメントがいい感じ

VBAのドキュメント
https://docs.microsoft.com/ja-jp/office/vba/api/overview/excel

GASのドキュメント
https://developers.google.com/apps-script/reference

それぞれ基本的なセルアクセスとかに関するドキュメントはあまり変わらない感じですね。

GASの方は公式日本語ドキュメントが見つけられてないので日本語限定だと少し難しいかも。
そういう意味ではGASの方が少しマイナスかもしれませんね。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String
↑ JSのStringドキュメント ここの関数はGASでも使えます

ただ、公式ドキュメント外の情報は圧倒的にGASが優勢です。

どういうことかというとGASではセルアクセスとかの関数はGoogle側より用意されているのですが、文字列とかはJSに備わっているものを使うようになっています。

なので文字列操作とかでわからない部分があったら「JS 文字列操作」みたいに調べればたくさん参考が出ます。

一方、VBAは文字列操作とかもMicrosoftで用意しているので調べるのが少し難しい感じですね。
「VBA 文字列操作」みたいにすれば出てはくるが言語自体の人口が多くはないと思うので、やってみた的なのは期待できない印象です。

正直、JS同様に文字列操作ができるだけでGASを選びます。

3. 無料で使える

言わずもがなですよね。

一方は有料でいい金額払って使うもので、一方は無料でいい感じに使えたら、そりゃ無料を選びますよね。

有料にしている分の良さがあることを否定はしませんがスクリプトで色々することを考えれば、圧倒的にGASです。
なにより、家でも勉強できるし趣味とかでスプレッドシート使うときに勉強が生かせるのがモチベになるから。

まぁ、どうせスプレッドシートで作ったものを範囲選択してエクセルにコピペしても大体いけるし、最終をエクセルにするとしても私はスプレッドシート一択です。

最後に

適当にVBAよりGASの方が好きなところを書いていましたが、1つだけデメリットがあります。

処理が遅い

これですね、VBAもそこまで早かった印象ないですがGASは割と遅いです。

まぁ、速度求めるならVBAも使わずに別プログラム作った方がいいのでは?と思ってしまうので、私は割り切って使っています。

速度面以外はGASかなーって感じですね。

メリット
- 癖が少ない 
- ドキュメントがいい感じ 
- 無料で使える

デメリット
- 処理が遅い 

色々、言いましたが結論コレです。

無料最高!!ヒャッホゥ!

私からは以上です。

iPhoneの画面交換しました

こんにちは!
すっかりまとめを書くのを忘れた、くろんです!

画面が映らなくなってしまったiPhone SEをやっと復活させることができたので、どんな感じだったか書きますー。

まずこちらです。

きれいな液晶してるだろ。映らないんだぜ。それで。

ということでケーブルをやらかしてしまい液晶が映らなくなってしまったiPhone SEです。

他の部分は大丈夫そうなので今回、液晶のみ取り換えを行いました。


目次

1. 注文パーツ

今回、買ったものはこちら。
↓ アマゾンリンク
(https://www.amazon.co.jp/gp/product/B07FXT9Y3Q )

届いたものがこちらです。
一通り分解用のツールと交換パーツのセットです。

個人的にいい感じだったツールはこれですね。
磁石になっていて外したねじをくっつけることで、どこから外したものか分からなくならないようにしてくれます。

以前、使っていたものは番号が振ってあるだけでぱっと見でどこかわからなかったのですが、こちらは写真付きなので分かりやすかったです。

2. 取り付け

ここから取り付けなのですが、今回写真を撮り忘れてしまったので文字で適当に書いておきます。

まず、分解の参考サイトはこちら
https://jp.ifixit.com/Guide/iPhone+SE+%E3%81%AELCD%E3%81%A8%E3%83%87%E3%82%B8%E3%82%BF%E3%82%A4%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%A4%E6%8F%9B/61310

ほとんど、参考サイトと同じ流れで分解を行い、
取り付けの際にはその逆順でつけていくだけなので特筆することはないですね。

なので、ここでは個人的に特に難しかった点をあげます。

ホームボタンの位置合わせ

キットに同封されていた説明書ではホームボタンを取り付ける時に位置を合わせると書いてあります。

このBがどうしても合わずに最終的にはBが少しずれた状態で固定しました。
ずれたせいで干渉している箇所などは見られなかったので大丈夫かと思いますが少し気持ちが悪いですね…

カメラ・スピーカー部分が出っ張る

こちらフロントカメラケーブル周りですね。
A-Eを位置合わせする必要があるのですがここも難しいですね。
まぁ、Bの箇所にガイドがあるのでそこで大まかに合わせて少しずつ微調節する感じで行けます。

私の場合、ここがどうしてもぴったりにくっつけられずに大変でした。

多分、DかEあたりがうまくついていないせいだと思ったのですが、カメラレンズ辺りが周辺より浮いた感じになってしまいました。

そのせいで一番最後フロントパネルを閉めるときに閉まらず、すべて外してやり直しを何度も繰り返しました。

個人的にはやり直し作業がすごいきつかったですね…
壊れるかもしれない不安が大きいので。

3. 取り付け終了

ともあれ、無事に液晶の交換ができました!
(モアレ酷い)

こんな感じになってます。

液晶に文字が重なっているのが、なんか良くてしばらくの間そのまま使っていました。

Ibかな?

フロントパネルは2300円くらいだったので今回の修理費用はそれくらいですね。
iPhone SEは修理がしやすくていいですね。

まだ、当分はスマホの乗り換え予定はないので騙し騙し使っていきたいですね。

iPhone SE2がこのサイズだったらなぁ。

4. おまけ

途中でポロって取り付けを諦めた謎パーツです!

多分、ここのEに入るはずなんですがこれ入れると浮いてどうしようもないので外しました。

環境光センサーに関係ありそうなパーツなので、もしそこら辺で問題があったら追記します。

では!!

iPhoneジャンクとのパーツ交換

こんにちは!
くろんです。

ジャンクのiPhoneが届いたので文鎮となってしまった元iPhone SEを復活させようと思います。

結論を先に言うと今回のパーツ交換はうまくいきませんでした…

どうしてそうなったのか、書いていきます。

まず、こちらが今回、文鎮となってしまったiPhone SEです。

どうでもいい事ですけど、
電源ボタンを押しても何の反応もしないiPhoneを触った時、
子供のころにUFOキャッチャーで取ったDSのパチモンをDSだと思いこんで喜んで箱を開けて、中身を見て泣きそうになった思い出を思い出しました…
まぁ、ゲームウォッチ的なゲームができたので少しは楽しんでましたけどね!!!

話が逸れましたが、文鎮化した経緯です。
touch IDの不調が見られたため開けて端子を確認しようとしたところ、ディスプレイ周りのケーブルを損傷しました。
開ける時に勢いがついてしまい、やらかした感じです。

場所としてはここですね。
なので、今回はこのケーブル周りを取り換え用と考えています。

ちなみに今回の分解はここ
https://jp.ifixit.com/Guide/iPhone+SE+%E3%81%AELCD%E3%81%A8%E3%83%87%E3%82%B8%E3%82%BF%E3%82%A4%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%A4%E6%8F%9B/61310
を参考に行っています。
こういう情報は本当に助かりますね。ありがとうございます!!

次にパーツを取る予定のジャンクを紹介

この時点で必要のないパーツは外していますが、こんな感じです。
iPhone SEは5/5s/SEとこのパーツは変わっていないので流用が可能です。多分

元iPhone SEに繋いでみるとこんな感じです。ちゃんとディスプレイがついているのでジャンクも壊れてないし、元iPhone SEの破損個所もここで確定ですね。

破損個所も特定したしジャンクも使えることが確認できたので、あとは付けたら終わりかと思いきや問題発生。

ここ外れないの!?

はい、割と一体になっているので初心者には外せませんね…
ジャンクは画面割れしているものなので画面ごと交換するのは嫌だし

これに手を出すと破損リスク高まるし、やっても見合ったリターンを得られなさそうなのでスルーで

…ということで

直せません!!終了です

普通に外れるものだと思ってましたねー。しゃあなしですね

ということで非正規品ですが、アマゾンでこちら
https://www.amazon.co.jp/gp/product/B07FXT9Y3Q
をポチりました

ケーブルのみ交換がいけるかなって思ってたんですけどね。画面割れと同じ対応になるみたいですね、フロントパネル箇所が壊れると。

ということで、こちらが明日届くのでこれ付けて終了させますー。

画面がつかなくなった場合のジャンクからのパーツ取りは画面割れしてないものじゃないとダメですね。注意
でもそれジャンクでないのでは??

ではー

OpenCVでスクリーンショット保存

こんにちは!
くろんです。

今回はOpenCVを使って画面のスクリーンショット画像を保存してみたいと思います。

まぁ、そこまで大変でもないので適当にコードとか載せていきます。

やること

1. WINAPIにてスクリーン情報取得
2. スクリーン情報より画面のピクセル情報を取る
3. OpenCVの画像フォーマットにピクセル情報を入れ込む

準備

- OpenCV準備(https://opencv.org/releases/からDL)
- 開発環境準備 -> VisualStudio2019使用
- 新規プロジェクト作成

OpenCVは今回、4.2.0を使用しました。

Windowsなら ↑ の赤枠からexe拾ってきて

展開フォルダ内のbuildから

bin\*.dll
include
x64\(VCバージョン)\*.lib

を自分のプロジェクトフォルダに持ってくると楽ですよ!

OpenCVを新規プロジェクトで使えるようにするためにはリンカーの設定を行います。

1. 新規プロジェクトを開き、「プロジェクト」タブ→「プロパティ」選択
2. 構成を「Release」に設定(Debugは同じく設定)
3. 「リンカー」→「全般」より「追加のライブラリディレクトリ」を選択
4. x64\(VCバージョン)\*.libより持ってきた*.libファイルがあるフォルダを選択
※opencv_world420.libとopencv_world420d.libというファイル名にdがついているものとそうでいないものがあると思うがこれらはRelease用かDebug用かで変わっている。dがついているものがDebug用

5. 「リンカー」→「入力」より「追加の依存ファイル」選択
6. 4.にて指定したフォルダに入っている*.libファイルをすべて依存ファイルとして指定
ex. Release用として設定していたら「 opencv_world420.lib」を追加

ついでにプロパティページにて「C/C++」内の「追加のインクルードディレクトリ」に includeフォルダを指定しておくと後で楽になります。

最後に動的ライブラリ(*.dll)を実行ファイルと同じ場所に置きます。
ビルドすると*.exeができるかと思うのでbin\*.dllをコピーします。

準備はこれでOKなはずです。

まとめ

- リンカーを設定
  - 追加のライブラリディレクトリ
  - 追加の依存ファイル
- 動的ライブラリ(*.dll)を実行ファイル(*.exe)と同じ場所に置く

コード書く

↓ 全体

HWND hwnd = GetDesktopWindow();
RECT rect;

GetWindowRect(hwnd, &rect);
unsigned int width, height;
width = rect.right;
height = rect.bottom;

BITMAPINFO bmpInfo;

bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = width;
bmpInfo.bmiHeader.biHeight = height;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;

LPDWORD lpPixel;
HBITMAP hBitmap;
HDC hMemDC;
HDC hdc = GetDC(hwnd);
hBitmap = CreateDIBSection(hdc, &bmpInfo, DIB_RGB_COLORS, (void**)&lpPixel, NULL, 0);

hMemDC = CreateCompatibleDC(hdc);
SelectObject(hMemDC, hBitmap);

BitBlt(hMemDC, 0, 0, width, height, hdc, 0, 0, SRCCOPY);

cv::Mat bMat = cv::Mat(height, width, CV_8UC3);
bMat.data = (uchar*)lpPixel;

cv::Mat flipImage;
cv::flip(bMat, flipImage, 0);

cv::imwrite("test.jpg", flipImage);

まず

HWND hwnd = GetDesktopWindow();
RECT rect;

GetWindowRect(hwnd, &rect);
unsigned int width, height;
width = rect.right;
height = rect.bottom;

ここらへんでスクリーンの幅・高さとディスプレイのハンドルを取得します。

次に

BITMAPINFO bmpInfo;

bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = width;
bmpInfo.bmiHeader.biHeight = height;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;

ここでBitmap形式で取得するときに必要な情報を設定します。(今回はOpenCVのMatフォーマットになるのでBitmap関係ないですがスクリーンの画素情報を取得するための儀式として書きます)

そして

LPDWORD lpPixel;
HBITMAP hBitmap;
HDC hMemDC;
HDC hdc = GetDC(hwnd);
hBitmap = CreateDIBSection(hdc, &bmpInfo, DIB_RGB_COLORS, (void**)&lpPixel, NULL, 0);

hMemDC = CreateCompatibleDC(hdc);
SelectObject(hMemDC, hBitmap);

Bitmap形式で画素を取得する準備を行います。

Bitmap形式でスクリーンの画素情報を取得するときにはBitBltを使用します。

BitBlt(hMemDC, 0, 0, width, height, hdc, 0, 0, SRCCOPY);

最後にOpenCVのMatフォーマットに入れ込みます

cv::Mat bMat = cv::Mat(height, width, CV_8UC3);
bMat.data = (uchar*)lpPixel;

cv::Mat flipImage;
cv::flip(bMat, flipImage, 0);

cv::imwrite("test.jpg", flipImage);

biBitCountで24(8bit3チャンネル)を指定していたのでMatフォーマットは CV_8UC3タイプを選択。

bMat.data はMatファイルの画素情報に当たる部分でそこにBitmap形式でスクリーン画素情報が入っている lpPixelの情報を入れています。

cv::flipで画像を上下反転しているのですがこれはデータの並びが逆なのでそのままだと反転して画像ができてしまうためです。

cv::imwriteは画像の保存をしてくれるOpenCVの関数です。

こんな感じにするとスクリーンショットをOpenCVのMatフォーマットにできます!!

※あっ、この方法だとディスプレイ設定で拡大表示とかして100%以外だとスクリーンの一部しか取得できない状態になるので気を付けてください。

↑ を回避するためにはもう少しごにょごにょやってあげる必要があります。