最近CNNを学習しつつ,C++で一からコーディング中です.
画像認識をさせたくてopenCVで画素データ取得しようと思ったのですが,コンパイル段階でかなり苦戦しました...
コードも画像読み込みと画素取得だけなのに,コンパイルが通らなくて大変でした.
色々調べてpkg-config
を使ったコンパイル方法などもあったのですが,うまく行かず.(後で原因わかりましたが)
おそらくopenCVをIDEではなくエディタ環境で使ってる人の多くが躓きそうだなぁと...
そこで,openCVの基本中の基本,画像読込み と 画素データ取得 のコードをコンパイルできるようにする事が目的です.
openCVを使いこなすことが目的ではなく,画素データ取得できればいいんだよ!という方向けです.
実行環境
OS : Arch Linux
OpenCV : 4.4.0
g++(GCC) : 10.1.0
実装
同一ディレクトリ内にあるsample.jpg
というデータのRGB情報を取得,表示するだけのコードです.
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat img, img_rgb;
img = cv::imread("sample.jpg");
// error handling
if(img.data == NULL) {
std::cerr << "Error: not found such file." << std::endl;
exit(1);
}
// convert BGR to RGB
cv::cvtColor(img, img_rgb, cv::COLOR_BGR2RGB);
// print pixel data
std::cout << img_rgb << std::endl;
}
cv::imread()
で読み込んだ画素データはBGRの順番になっています.そこでcv::cvtColor()
でBGRをRGBに並び替えてます.そのままでも良い方はcv::cvtColor()
は不要です.
コンパイル
上記コード用のMakefileです.
# Makefile
FILE = hoge.cpp
HDIR = -I /usr/include/opencv4
OPT = -lopencv_core -lopencv_imgproc -lopencv_imgcodecs
a.out: $(FILE)
$(CXX) -Wall $(FILE) $(HDIR) $(OPT)
ソースファイル名がsample.cpp
なら,上記3行目をFILE = sample.cpp
と書き換えるか,実行時にmake FILE=sample.cpp
としてください.
これで実行ファイルが作成されると思います.
もしコードにそのほかのopenCVの関数を使った場合は,適宜オプションを追加してください.
これでコンパイル時に悩まされないと思います.
pkg-config
を使うとMakefileを使わずにできますが,個人的にMakefileが好きという事もありMakefileで記述しました.
おまけ
連番になっている画像の画素データをファイルに書き出したかったので,そのコードも載せておきます.
ファイル名をsample_0.jpg,...,sample_(n).jpg
の連番形式,画像サイズを28x28とします.
#include <iostream>
#include <fstream> // for ofstream
#include <opencv2/opencv.hpp>
#define PIXEL 28 // image size
#define CHNEL 3 // number of channel [RGB = 3]
int main() {
cv::Mat img, img_rgb;
std::ofstream ofs("pixel_data.txt");
int idx = 0; // for file index
while((img = cv::imread("sample_"+std::to_string(idx)+".jpg", 1)).data) {
// convert BGR to RGB
cv::cvtColor(img, img_rgb, cv::COLOR_BGR2RGB);
// output pixel data to file
for(int i = 0; i < CHNEL; i++) {
for(int m = 0; m < PIXEL; m++) {
for(int n = 0; n < PIXEL; n++) {
ofs << static_cast<int>(img_rgb.at<cv::Vec3b>(cv::Point(n, m))[i]);
if(n != PIXEL-1) ofs << " ";
}
ofs << "\n";
}
}
idx++;
}
std::cout << "outputting pixel data to file completed." << std::endl;
}
簡単に説明します.
出力形式は空白区切り,データの並びは各ファイルごとにRGBの順に出力されます.
実際に画素データを取得,出力してるのは,21行目の
ofs << static_cast<int>(img_rgb.at<cv::Vec3b>(cv::Point(n, m))[i]);
です.
Matクラスのat関数を用いて,Channel[i]の(n, m)番値の画素データを取得,intにキャスとした値をファイルに出力,という事をしています.
cv::Point(n, m)
が番地を指定しているのですが,(m, n)にするとMatクラスで取得した行列の転置行列になってしまうので,(n, m)にしています.
もし簡単なファイル出力方法などあれば,教えていただけると幸いです.