ちまたには,OpenCVを使って○○しましたというWeb上の記事がたくさんありますね.楽しそうだなあと横目に見る日々を過ごしていたのですが,2015年の締めくくりにexStickとOpenCVで遊んでみる事にしました.
実験環境
実験環境はexStickとOV7670です.exStickのFPGAには,100MbpsのネットワークをつかってVGA画像をPCに送り続ける簡単なハードウェアが実装されています.

OpenCVと組み合わせて画像処理
これまでは,「UDPで送られてくるデータを組み立てて動画像として表示する」という簡単なフロントエンドを実装していました.今回は,一歩すすんで,受信したデータをOpenCVのイメージデータとして表示,処理するというのを試してみました.
結果は次のように.順に,”そのまま表示”,”グレースケール化”,”エッジ検出”,”人の顔抽出”です.パソコンの処理性能ってすごいですね.

ソースコード
ソースコードはこんな感じ.opencv_client.cxx
メインの部分は次の通りです.UDPパケットを組み立てるところ以外は,よくあるOpenCVのサンプルです.顔検出用のデータはOpenCV 2.11に同梱のものを使いました.本当に便利ですね.
今回は手抜き実装で,画像の受信と処理を逐次に行なっているため,取りこぼしは発生するのですが,目をつぶることにします.それでも,カメラの前で手を振ってみてもそう違和感はありませんでした.
for(;;){
for(int i = 0; i < 480; i++){
numrcv = recv(sock, buf, 2048, 0);
int y = toInt(buf, 0);
for(int j = 0; j < 640 * 2; j++){
raw[y * 640 * 2 + j] = buf[j + 4];
}
}
int r, g, b, y0, y1, cb, cr;
for(int i = 0; i < 320; i++){
for(int j = 0; j < 480; j++){
y0 = ((int)raw[j*640*2+4*i+0]) & 0x000000FF;
cb = ((int)raw[j*640*2+4*i+1]) & 0x000000FF;
y1 = ((int)raw[j*640*2+4*i+2]) & 0x000000FF;
cr = ((int)raw[j*640*2+4*i+3]) & 0x000000FF;
r = (int)(y0 + 1.40200 * (cr - 128));
g = (int)(y0 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128));
b = (int)(y0 + 1.77200 * (cb - 128));
r = r < 0 ? 0 : r; r = r > 255 ? 255 : r;
g = g < 0 ? 0 : g; g = g > 255 ? 255 : g;
b = b < 0 ? 0 : b; b = b > 255 ? 255 : b;
rgb[3*((2*i)+640*j)+0] = b;
rgb[3*((2*i)+640*j)+1] = g;
rgb[3*((2*i)+640*j)+2] = r;
r = (int)(y1 + 1.40200 * (cr - 128));
g = (int)(y1 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128));
b = (int)(y1 + 1.77200 * (cb - 128));
r = r < 0 ? 0 : r; r = r > 255 ? 255 : r;
g = g < 0 ? 0 : g; g = g > 255 ? 255 : g;
b = b < 0 ? 0 : b; b = b > 255 ? 255 : b;
rgb[3*((2*i+1)+640*j)+0] = b;
rgb[3*((2*i+1)+640*j)+1] = g;
rgb[3*((2*i+1)+640*j)+2] = r;
}
}
cv::Mat image = cv::Mat(Size(640, 480), CV_8UC3, rgb);
cv::imshow("Receive", image);
cv::Mat gray_image;
cv::cvtColor(image, gray_image, CV_BGR2GRAY, 0);
cv::imshow("Gray", gray_image);
cv::Mat sobel_x, sobel_y, sobel;
Sobel(gray_image, sobel_x, CV_32F, 1, 0);
Sobel(gray_image, sobel_y, CV_32F, 0, 1);
sobel = (abs(sobel_x) + abs(sobel_y)) / 2.0;
cv::threshold(sobel, sobel, 100, 255, THRESH_BINARY_INV);
cv::imshow("Sobel", sobel);
faces.clear();
cascade.detectMultiScale(image, faces, 1.1, 2, 0, Size(4, 4));
for (int i = 0; i < faces.size(); i++){
rectangle(image,
Point(faces[i].x,faces[i].y),
Point(faces[i].x + faces[i].width,faces[i].y + faces[i].height),
Scalar(0, 200, 0),
3,
CV_AA);
}
cv::imshow("Face", image);
cvWaitKey(10);
}
まとめ?
やってみましたー,という話で特にまとめというものはないのですが,exStickから受信した動画像をOpenCVを使ってオンタイムで処理するのは,なかなか楽しいものです.次のステップとしては,一部のHW化とか,活用事例とか考えてみたいなと思っています.


コメント