[OpenCV]OpenCVペイント、イベント処理、ROI&マスク計算


描画


💜 直線()ちょくせん()ちょくせん()
💜 長方形()ちょうほうけい()ちょうほうけい:長方形を描画ちょうほうけいをえがく
💜 circle()circle()circle()circle()circle()circle()circle()circle()circle()circle()circle()circle()circle()circle()circle
💜 ポリライン()ポリライン():ポリゴンを描画ぽりごんをぺいん
💜 putText():文字列を挿入
VideoCapture cap("test_video.mp4");

if (!cap.isOpened()) {
    cerr << "Video open failed!" << endl;
    return -1;
}

Mat frame;
while (true) {
    cap >> frame;

    if (frame.empty()) {
        cerr << "Empty frame!" << endl;
        break;
    }

    line(frame, Point(570, 280), Point(0, 560), Scalar(255, 0, 0), 2);
    line(frame, Point(570, 280), Point(1024, 720), Scalar(255, 0, 0), 2);

    int pos = cvRound(cap.get(CAP_PROP_POS_FRAMES));    // 현재 프레임 번호
    String text = format("frame number: %d", pos);
    putText(frame, text, Point(20, 50), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 0, 255), 1, LINE_AA);

    imshow("frame", frame);

    if (waitKey(10) == 27)
        break;
}

cap.release();
destroyAllWindows();

イベントの処理


キーボードイベント


💜 waitKey():キーボード入力待ち
パラメータdelayはミリ秒単位のヒステリシス時間であり、default値は0に設定される.0以下は無限待ちです.キーボード入力を受信すると、押したキー値はint型に戻り、そうでない場合は-1に戻ります.
正常に動作するにはOpenCVウィンドウが必要です.imshow()関数が呼び出された後にのみ、画面にビデオが表示されます.
特殊キー(矢印、Fnキーなど)は、waitKeyEx()関数を使用します.

マウスイベント


💜 setMouseCallback():マウスイベントを処理するコールバック関数を登録
コールバック関数はtypedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata)形式で定義する必要があります.
Mat src;
    // main과 callback 함수 모두에서 쓰기 위해 전역변수 선언
Point ptOld;

void on_mouse(int event, int x, int y, int flags, void*);

int main(void)
{
	src = imread("lenna.bmp");
	
	namedWindow("src");
	setMouseCallback("src", on_mouse);  // 창 이름이 먼저 지정되어 있어야 namedWindow를 위에 먼저 선언해줌

	imshow("src", src);
	waitKey();
}

void on_mouse(int event, int x, int y, int flags, void*)    //사용자 지정 데이터인 userdata를 쓰지 않을 예정이라 void* 뒤 이름 선언 안함
{
	switch (event) {
	case EVENT_LBUTTONDOWN: // 왼 버튼이 눌리면
		ptOld = Point(x, y);
		cout << "EVENT_LBUTTONDOWN: " << x << ", " << y << endl;
		break;
	case EVENT_LBUTTONUP:   // 왼 버튼이 눌렸다 떼어지면
		cout << "EVENT_LBUTTONUP: " << x << ", " << y << endl;
		break;
	case EVENT_MOUSEMOVE:   // 마우스가 움직이면
		if (flags & EVENT_FLAG_LBUTTON) {   
			cout << "EVENT_MOUSEMOVE: " << x << ", " << y << endl;
			line(src, ptOld, Point(x, y), Scalar(0, 255, 255), 3, LINE_AA);
			ptOld = Point(x, y);
			imshow("src", src);
		}
		break;
	default:
		break;
	}
}
グローバル変数srcとして宣言したが、コールバック関数にuserdata因子を加えるように設定した場合、main()で宣言して領域変数として使用することができる.if (flags & EVENT_FLAG_LBUTTON)から==までの演算子をflags == EVENT_FLAG_LBUTTONと書くこともできますが、EVENT_FLAG_LBUTTONは1として定義されているので、flag&(and)演算でTrueを返すかどうかを確認することが望ましいです.==と書けば、ctrlキーのようにマウスを押すと動かなくなります.ctrlの身長は8なのでtrueではありません.
マウスイベントの速度が遅いため、マウスの前の位置を記録し、前の点から現在の点まで直線を描画する必要があります.これがptOld変数と書かれている理由です.この処理がなければ,線は断続的に出てくる.

トラックバー(スライダコントロール)トラックバースライダコントロール


💜 createTrackbar():trackbar作成関数trackbarさくせいかんすう
trackbarの位置が変化するたびに、呼び出されるコールバック関数はtypedef void (*TrackbarCallback)(int pos, void* userdata)フォーマットであるべきである.
void on_level_change(int pos, void* userdata);

int main(void)
{
	Mat img = Mat::zeros(400, 400, CV_8UC1);

	namedWindow("image");
	createTrackbar("level", "image", 0, 16, on_level_change, (void*)&img);
        // 세 번째 인자인 value(값을 받아올 곳)을 0(Null)로 하면 반드시 callback 함수를 지정해야 함

	imshow("image", img);
	waitKey();
}

void on_level_change(int pos, void* userdata)
{
	Mat img = *(Mat*)userdata;  // 원본과 동일한 영상 데이터를 가리킴

	img.setTo(pos * 16);    //256이 255보다 크기 때문에 255(white)로 출력이 됨
	imshow("image", img);
}

関心領域とマスク演算


対象領域(ROI)は、任意の演算を行う任意の領域です.ROI機能をサポートするOpenCV関数を記述するために、マスクビデオをパラメータとして渡す必要がある場合があります.マスク画像は、階調CV 8 UC 1タイプであり、通常、0または255(黒または白)の画素値を持つバイナリ画像(バイナリ画像)が用いられる.
例えば、copyTo()関数は、2番目のパラメータmaskに1つのマスク画像を与えることができ、マスク画像のゼロ以外の部分でのみcopyが発生する.
Mat src = imread("airplane.bmp", IMREAD_COLOR);
Mat mask = imread("mask_plane.bmp", IMREAD_GRAYSCALE);
Mat dst = imread("field.bmp", IMREAD_COLOR);

src.copyTo(dst, mask); //또는 copyTo(src, dst, mask);

上図左からsrcmaskdstの映像です.飛行機部分のみをマスク演算し、dstに貼り付け、以下に示す.

Alphaチャネルを持つpngファイルを使用してマスク演算を練習します.pngファイルは、B、G、R、Aの4チャネル画像であり、alphaチャネルは0であり、RGB値のない透明な空間が埋め込まれ、色があれば255である.したがって、alphaチャネルをマスクとして使用することができる.
Mat src = imread("cat.bmp", IMREAD_COLOR);
Mat logo = imread("opencv-logo-white.png", IMREAD_UNCHANGED);

vector<Mat> planes;
split(logo, planes);    // 채널 분할

Mat mask = planes[3];   //알파 채널이 4번째
merge(vector<Mat>(planes.begin(), planes.begin() + 3), logo);
    // 앞에서 3개만 따다가 새로운 vec<Mat> 객체를 만듦. BGR 만 있을 것. 그것을 logo라는 Mat 객체로 만듦
Mat crop = src(Rect(10, 10, logo.cols, logo.rows));
    // cat 영상이 더 크니까 로고 들어갈 부분만 잘라서 저장해둠. 참조이므로 이것을 변경하면 원본도 변경됨

logo.copyTo(crop, mask);    // 둘이 크기와 타입이 같으므로 새로 만들어지는 것이 아니라 crop에 직접 픽셀 들어감