2019年7月11日木曜日

ResNet-50をやってみた

先日作った、最小のOpenCVのNewralNetwork Runtime。
これ、いろんなONNXファイルを読みこめるので、いろんなディープラーニングを動かすことができます。

ディープラーニングの部分は以下のほぼ3行だし。
net=readNetFromONNX()
net.setInput()
net.forward();

今日は以下のサイトを参考に、ResNetという写真に写っているものが何かを認識するプログラムを作ってみたいと思います。

https://rest-term.com/archives/3503/

ResNetはこんなにつながってるらしいです。


これつなげるだけで間違うな。
でもいつものようにつなげて学習済みのデータを取ってくれば誰でもできます。


まず、先日作ったいつもの最小のNewralNetwork Runtimeを取ってきます。
https://drive.google.com/file/d/1P4VC3_gZXwANeL0yup-5Ni56IFyvs_VM/view?usp=sharingminoipencv


次に、ResNet-50の学習済みデータを取ってきます。
https://github.com/onnx/models/tree/master/models/image_classification/resnet

ディープラーニングの学習済みデータってどれも何百Mバイトもデータがあります。
ほんとうにすぐにハードディスクなくなってしまうじゃないか。

OpenCVのDNNライブラリはドキュメントがまだきちんと整備されていないのでいろいろはまるところがあります。
ソースコードやONNXの仕様書を読んでいろいろパラメーターを調べていくと、どうやらこのコードが正しそう。

---------------------------------
#include<stdio.h>
#include<iostream>
#include<string>
#include<vector>

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION


#include "opencv2_core.hpp"
#include "opencv2_imgproc.hpp"
#include "opencv2_imgproc_imgproc_c.h"
#include "opencv2_dnn.hpp"

#include "stb_image.h"
#include "stb_image_write.h"

void changeb_g(unsigned char* p, int x, int y, int c)
{
int ct = x*y;
int i;
unsigned char t;
for (i = 0; i < ct; i++) {
t = p[0];
p[0] = p[2];
p[2] = t;
p[3] = 255;
p += c;
}
}


int main()
{
unsigned char* p;
int x=-1, y=-1, n=-1;

//
p = stbi_load("beer.jpg", &x, &y, &n, 4);
if (p == NULL || x < 1 || y < 1)return 1;
changeb_g(p, x, y, 4);
cv::Mat color = cv::Mat(y, x, CV_8UC4);
memcpy(color.data, p, x * 4 * y);
stbi_image_free(p);

//
cv::Mat img;
cv::cvtColor(color, img, CV_BGRA2BGR);
cv::resize(img, img, cv::Size(224, 224));

cv::Mat inpBlob = cv::dnn::blobFromImage(img, 1.0 / 255.0, cv::Size(224, 224), cv::Scalar(104, 117, 123));
cv::dnn::Net net = cv::dnn::readNetFromONNX("resnet50v1.onnx");
net.setInput(inpBlob);
cv::Mat output = net.forward();

cv::Mat sorted(output.rows, output.cols, CV_32F);
cv::sortIdx(output, sorted, CV_SORT_EVERY_ROW | CV_SORT_DESCENDING);
cv::Mat topk = sorted(cv::Rect(0, 0, 5, 1));

int no;

//std::cout << "output = " << std::endl << " " << output  << std::endl << std::endl;
//std::cout << "topk = " << std::endl << " " << topk << std::endl << std::endl;

static char name[1000][256];
char buf[256];
int i;
FILE* fp;
char* s;
fp = fopen("synset.txt", "rt");
if (fp) {
for (i = 0; i < 1000; i++) {
buf[0] = 0;
fgets(buf, 256, fp);
s = strstr(buf, "\r");
if (s)*s = ' ';
s = strstr(buf, "\n");
if (s)*s = ' ';
strcpy(name[i], buf);
}
}

for (i = 0; i < 5; i++) {
no = topk.at<int>(i);
printf("%d  %d  %s  %f\n",i+1, no, name[no], output.at<float>(no));
}

return 0;
}
---------------------------------

それではビールのグラスの絵を認識させてみます。


結果
---------------------------------
1  441  n02823750 beer glass   18.468941
2  572  n03443371 goblet   13.881867
3  440  n02823428 beer bottle   10.277216
4  969  n07932039 eggnog   10.136989
5  898  n04557648 water bottle   9.364053
---------------------------------

昨日は朝から晩まで飯も食わず一日中デバッグしてもできなかったんですが、おーなんかできた気がする。

ONNXでディープラーニングって数行コード書くだけでいろいろなとができて楽しいね。
OpenCVのNewralNetwork Runtimeはまだネットで検索してもあまり出てきませんが、とっても軽量で使い方も簡単。
C++コンパイラ以外何にもインストールなしで手軽にディープラーニングができて面白いですね。



0 件のコメント:

コメントを投稿