[w 6 d 5]検出Huff変換


検出Huff変換直線


image space: (x,y)
parameter space: (a,b)
まず,直線を通過する点を検出するためには空間を定義しなければならない.y=ax+bは(x,y)をパラメータとして用い,aとbは斜めとyスライスで直線の形状を決定する.対照的に,b=−ax+yは(a,b)をパラメータとし,(x,y)は直線の形状を決定因子として用い,まず空間を定義して直線を通過する点を検出する.y=ax+bは(x,y)をパラメータとして用い,aとbは斜めとyスライスで直線の形状を決定する.対照的に,b=−ax+yはパラメータとして(a,b),(x,y)は決定因子として直線の形状を用いる.
(x,y)空間における2点(x 1,y 1)、(x 2,y 2)を通過する直線をy=a 1 x+b 1と呼ぶ.
(a,b)空間ではb=−x 1 a+y,b=−x 2 a+y直線が(a 1,b 1)で交差する.

しかし,(a,b)空間を用いる欠点は垂直線を含まないことであり,多くの方程式を表現するために傾斜値は(−inf,inf)の範囲であり,非常に広い.これを改善するために,Hesseパターンを用いて式を以下に示す.


Hesseパターンを使用して、一点の複数の直線を通して次の画像に見ることができます.

直線検出のためにHuff変換を適用するとrho,theta空間に三角関数のような形が現れる.次に、検出時に結果を表示する画像を示します.まず、全体を0に設定した(rho,theta)マトリクスを作成し、各点を通る曲線の画素値を1に上げることができます.明るい点は、多くの曲線が通過すること、すなわち、多くの点が共に通過する直線を意味する.

この画像をより詳細に見るために、cannyを適用すると、Hesse normal formを用いた空間において各点を通過する直線を複数の色で表し、以下のような形状になる.

1本の直線上の多くの点は右図の様々な色の曲線で表される.各カーブは、その点を通る直線を表し、色が交差する場所はすべてのカーブ、すなわち複数の点を通る共通の直線を表します.上図は2つの点が発生し、2つの直線が検出されたことを示しています.
void cv::HoughLines 	( 	InputArray  	image,
		OutputArray  	lines,
		double  	rho,
		double  	theta,
		int  	threshold,
		double  	srn = 0,
		double  	stn = 0,
		double  	min_theta = 0,
		double  	max_theta = CV_PI 
	) 		
OpenCVでは,直線検出には2種類のHuff変換直線検出関数が存在する.Input画像には階調エッジ画像を追加する必要があり、cv::Canny関数を適用した後に使用してもよいし、Sobelフィルタリングの結果を使用してもよい.cv::HoughLines関数で、直線上に直線パラメータ(rho,theta)を格納する出力ベクトル(std::vector)を指定します.rho,thetaはその空間における間隔を表し,閾値は蓄積アレイから直線的に判断される閾値を表す.min thetaとmax thetaを使用して角度領域を制限できます.
void cv::HoughLinesP 	( 	InputArray  	image,
		OutputArray  	lines,
		double  	rho,
		double  	theta,
		int  	threshold,
		double  	minLineLength = 0,
		double  	maxLineGap = 0 
	) 	
cv::HoughLinesp関数は角度に制限されず、結果値を直線の2つの端点として受け入れ、画像の表示を容易にします.linesはstd::vectorをパラメータとして受け入れ、lines[0]、lines[1]、lines[2]、lines[3]はそれぞれx 1、y 1、x 2、y 2に対応する.minLineLengthは最小の直線長を決定し、maxLineGapは直線に一定の距離があっても直線と見なすことを決定する.
#include <iostream>
#include "opencv2/opencv.hpp"
#include <vector>

int hough_thresh=160,hough_minlinelength=50,hough_maxlinegap=50;
double hough_maxlinegap_double;

void on_change(int, void*);

int main()
{
    cv::VideoCapture cap("../resources/test_video.mp4");

    if (!cap.isOpened()){
        std::cerr << "video load failed!" << std::endl;
        return -1;
    }

    //trackbar
    cv::namedWindow("roi_hough");
    cv::createTrackbar("threshold: ","roi_hough",&hough_thresh,1000);
    cv::createTrackbar("min_line_length: ","roi_hough",&hough_minlinelength,500);
    cv::createTrackbar("max line gap: ","roi_hough",&hough_maxlinegap,100,on_change);

    cv::Mat frame,frame_canny,roi_canny,roi_hough;
    std::vector<cv::Vec4i> lines;
    cv::Mat mask(cap.get(cv::CAP_PROP_FRAME_HEIGHT),cap.get(cv::CAP_PROP_FRAME_WIDTH),CV_8UC1,cv::Scalar(0));
    cv::Mat frame_hough = mask.clone();
    roi_hough = mask.clone();

    std::vector<cv::Point> mask_pts(5);

	mask_pts[0] = cv::Point(400, 340);
    mask_pts[1] = cv::Point(680, 340);
	mask_pts[2] = cv::Point(1200, 720);
    mask_pts[3] = cv::Point(0, 720);
    mask_pts[4] = cv::Point(0, 480);

    cv::fillPoly(mask,mask_pts,cv::Scalar(255));
    
    while(true){
        cap >> frame;

        if (frame.empty()){
            std::cout << "empty frame" << std::endl;
            break;
        }

        cv::Canny(frame,frame_canny,50,150);
        frame_canny.copyTo(roi_canny,mask);
        cv::HoughLinesP(frame_canny,lines,0.5,CV_PI/180,hough_thresh,hough_minlinelength,hough_maxlinegap_double);
        frame_hough.setTo(0);
        roi_hough.setTo(0);
        for (auto& line : lines){
            cv::line(frame_hough,cv::Point(line[0],line[1]),cv::Point(line[2],line[3]),cv::Scalar(255),2);
        }
        frame_hough.copyTo(roi_hough,mask);

        cv::imshow("frame",frame);
        cv::imshow("roi_canny",roi_canny);
        cv::imshow("roi_hough",roi_hough);
        
        if(cv::waitKey(30)==27) break;
    }
    cap.release();
    cv::destroyAllWindows();

    return 0;
}

void on_change(int, void*){
    hough_maxlinegap_double = 0.1*hough_maxlinegap;
    // TrackBar는 정수단위만 되는데, 조금 더 세부적으로 조정하기 위해 0.1 단위로 바꿔줌.
}


検出Huff変換源


円を検出できます.2 D空間でグラデーション方向に直線を描画して蓄積画像を生成すると、円の中心に大きな値が表示されます.この中心に基づいて半径を増やし、適切な半径を検出することによって円を検出することができる.
void cv::HoughCircles 	( 	InputArray  	imageCircles
		OutputArray  	circles,
		int  	method,
		double  	dp,
		double  	minDist,
		double  	param1 = 100,
		double  	param2 = 100,
		int  	minRadius = 0,
		int  	maxRadius = 0 
	) 	
OpenCVはcv::HoughCirclesを使用して円を検出できます.注意edgeビデオではなく、入力ビデオを通常のビデオに渡す必要があります.方法はHOUGH GRADIENTとHOUGH GRADIENT ALTがあり,paramを用いる方法が異なる.dp入力画像と蓄積アレイの大きさ比率を高い値に設定すると、蓄積アレイの大きさが縮小され、演算量が減少する.HOUGH GRADIENT方式では、param 1はCanny edge検出器の高閾値であり、param 2は蓄積アレイの閾値である.HOUGH GRADIENT ALT法では、Scharrフィルタを使用しているため、Param 1値をより高く設定する必要があり、Param 2値は円の完全性(1.0に近い)を表す.
https://ko.wikipedia.org/wiki/%ED%97%88%ED%94%84_%EB%B3%80%ED%99%98
https://en.wikipedia.org/wiki/Hough_transform
https://en.wikipedia.org/wiki/Circle_Hough_Transform