C++
OpenCV

OpenCV(C++)の画像処理メモ

研究室の都合で使う機会があったので、色々な画像処理手法のメモ
ただしC++のOpenCV

コンパイル

環境c++, Mac

g++ -std=c++11 @@@.cpp -o @@@ `pkg-config --cflags opencv` `pkg-config --libs opencv`

include

#include <opencv2/opencv.hpp>

画像読み込み

RGB読み込み

cv::Mat img = cv::imread("img.jpg", 1)

グレースケール読み込み

cv::Mat img = cv::imread("img.jpg", 0);

高さ、幅の取得

int width = img.rows;
int height = img.cols;

リサイズ

画像サイズを半分にリサイズする

cv::Mat out;
cv::resize(img, out, cv::Size(), 0.5, 0.5);

切り抜き

画像から切り抜きする方法。これでoutに切り抜いた画像が入る。

cv::Rect rect = cv::Rect(left-top-x, left-top-y, width, height);
cv::Mat out(orig_image, rect);

画像を見るための関数

void visualize(Mat img, double scale=0.4) {
    Mat temp;
    resize(img, temp, Size(), scale, scale);
    imshow("", temp);
    waitKey(0);
}

RGB成分に分解

opencvのチャンネルはbgrの順番

cv::Mat channels[3], red, blue, green;
cv::split(img, channels);
green = channels[0];
blue = channels[1];
red = channels[2];

cvtColor

グレースケール

cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

HSV変換

cv::Mat hsv;
cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);

それぞれの成分に分解する

cv::Mat channels[3];
cv::split(hsv_img, channels);

Scalar

B,G,Rの順に0~255で使う。

cv::Scalar(blue, green, red);

二値化

大津の二値化

cv::Mat out;
cv::threshold(gray, out, 30, 255, CV_THRESH_OTSU);

エッジ抽出

Canny

cv::Mat out;
cv::Canny(gray, out, gray.rows*0.1, gray.rows*0.1, 3, false);

Sobel

CV_8Uを指定しているのは、cv::imshow()ですぐ表示できるようにするため!

cv::Mat out;
cv::Sobel(gray, out, CV_8U, 0, 1);

Laplacian

CV_8Uを指定しているのは、cv::imshow()ですぐ表示できるようにするため!

cv::Mat out;
cv::Laplacian(gray, out, CV_8U, 3);

フィルタリング

ガウシアンフィルター

cv::Mat out;
cv::GaussianBlur(img, out, Size(3, 3), 0);

任意のフィルタリング

cv::Mat out;
double k = 3;
cv::Mat kernel = (cv::Mat_<float>(3, 3) <<
    -k / 9.0f,   -k / 9.0f,           -k / 9.0f,
    -k / 9.0f,   1 + (8 * k) / 9.0f,  -k / 9.0f,
    -k / 9.0f,   -k / 9.0f,           -k / 9.0f);
cv::filter2D(img, out, -1, kernel, Point(-1, -1), 0, BORDER_DEFAULT);

収縮・膨張

モルフォロジー処理

例えばCannyの結果にモルフォロジー処理したい時

cv::Mat out;
cv::morphologyEx(canny, out, MORPH_CLOSE, Mat(), Point(-1, -1), 1);

dilate(膨張)

cv::Mat out;
cv::dilate(edge, out, Mat(), Point(-1, -1), 1);

erode(縮小)

cv::Mat out;
cv::erode(edge, out, Mat(), Point(-1, -1), 1);

直線検出

Line Segment Detector

直線検出の手法・Line-Segment-Detectorの使い方
ただし、OpenCV-3(3.1?)以降でのみ使用可能
例えばCannyからの〜直線を検出する

Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
std::vector<cv::Vec4f> lines;
ls->detect(canny, lines);

std::vector<Vec4f>::iterator it = lines.begin();
for (; it != lines.end(); ++it) {
    cv::Vec4f l = *it;
    cv::line(img, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 1, CV_AA);
}

ハフ変換(Hough)

Cannyからの〜ハフ変換(直線検出)

std::vector<cv::Vec2d> lines;
cv::HoughLines(canny, lines, 1, PI/180.0, 100, 0, 0);

std::vector<cv::Vec2d>::iterator it = lines.begin();
for(; it!=lines.end(); ++it){
    double rho = (*it)[0], theta = (*it)[1];
    cv::Point pt1, pt2;
    double a = cos(theta), b = sin(theta);

    double x0 = a * rho, y0 = b * rho;
    pt1.x = saturate_cast<int>(x0 + 1000 * (-b));
    pt1.y = saturate_cast<int>(y0 + 1000 * a);
    pt2.x = saturate_cast<int>(x0 - 1000 * (-b));
    pt2.y = saturate_cast<int>(y0 - 1000 * a);

    cv::line(img, pt1, pt2, Scalar(0, 0, 255), 1, CV_AA);
}

確率的ハフ変換(Probabilistic-Hough)

Cannyからの〜確率的ハフ変換(直線検出)

std::vector<cv::Vec4i> hough_ls;
cv::HoughLinesP(canny, hough_ls, 1, cv::CV_PI / 180, 50, 50, 500);
std::vector<cv::Vec4i>::iterator ite = hough_ls.begin();
for (; ite != hough_ls.end(); ++ite) {
    cv::Vec4i l = *ite; 
    cv::line(img, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 1, CV_AA);
    cv::Scalar(0, 0, 255), 1, CV_AA);
}

描画

テキスト

putText(img, "text", Point(100, 200), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1, CV_AA);

矩形

rectangle(img, Point(left-top-x, left-top-y), Point(right-down-x, right-down-y), Scalar(255, 0, 0), 5, CV_AA);

circle(img, Point(center-x, center-y), radius, Scalar(0, 0, 255), -1);

Connected Componentsに分ける

二値化からの〜Connected Componentsに分ける

// CC draw
Mat labelImg(img.size(), CV_32S);
int nLabels = cv::connectedComponents(binary, labelImg, 8);
std::vector<cv::Vec3b> colors(nLabels);
colors[0] = cv::Vec3b(0, 0, 0);
for(int label = 1; label < nLabels; ++label){
    colors[label] = cv::Vec3b((rand()&255), (rand()&255), (rand()&255));
}

  // ラベリング結果の描画
cv::Mat dst(img.size(), CV_8UC3);
for(int y = 0; y < dst.rows; ++y){
  for(int x = 0; x < dst.cols; ++x){
    int label = labelImg.at<int>(y, x);
    cv::Vec3b &pixel = dst.at<cv::Vec3b>(y, x);
    pixel = colors[label];
  }
}

DFT & IDFT (?)

かもしれないのでご存じの方は教えてください
最後はCV_8Uにすることで可視化できる

Mat gray_f, dft, effected, idft, result;
gray.convertTo(gray_f, CV_32FC2);
cv::dft(gray_f, dft);
effected = dft;
cv::dft(effected, idft, cv::DFT_INVERSE | cv::DFT_SCALE);
idft.convertTo(result, CV_8U);