박민혀기

CELLON Kart Source Code 1.3.5v(YCrCb_ROI) (2023.08.30) 본문

CELLON Kart(Tracking)/Source Code

CELLON Kart Source Code 1.3.5v(YCrCb_ROI) (2023.08.30)

박민혀기 2023. 8. 30. 22:46

Update Feature

  • HSV Model -> YCrCb Model
  • StaticROI -> CustomROI

Next Develop

  • AutoFocus 카메라 사용시 샤프닝 적용 -> 적용 반응 미미
  • Stabilization
  • Size to HSV ratio
#include <iostream>
#include <cmath>
#include <vector>
#include <opencv2/opencv.hpp>

#define COUNT_CONTOUR	3	//MIN_CONTOUR >= COUNT_CONTOUR

using namespace std;
using namespace cv;

Mat frame, ycc_frame;
Scalar meanYCC = Scalar(0, 0, 0);
int  Adaptive_Cnt = 0;

bool comparePointVectors(const vector<Point>& a, const vector<Point>& b) {
	return a.size() > b.size();
}

vector<vector<Point>> SortingArea(vector<vector<Point>> SortingContours) {
	sort(SortingContours.begin(), SortingContours.end(), comparePointVectors);

	return SortingContours;
}

Scalar pixels_avg(vector<Scalar> pixels) {
	Scalar avgYCC(0, 0, 0);

	for (int i = 0; i < pixels.size(); i++)
		avgYCC += pixels[i];

	avgYCC /= static_cast<float>(pixels.size());
	return avgYCC;
}

int Close_YCC_Pro(vector<Scalar> Compare_YCC_List) {
	int Close_YCC_Index = 0;
	int Close_YCC_Dis, Close_YCC_Dis_List;
	Scalar Close_YCC = Compare_YCC_List[0];
	Scalar Close_YCC_Min, Close_YCC_Min_List, Close_YCC_List;

	for (int i = 1; i < Compare_YCC_List.size(); i++) {
		Close_YCC_Min = meanYCC - Close_YCC;
		Close_YCC_Dis = abs(Close_YCC_Min[0]) + abs(Close_YCC_Min[1]) + abs(Close_YCC_Min[2]);

		Close_YCC_Min_List = meanYCC - Compare_YCC_List[i];
		Close_YCC_Dis_List = abs(Close_YCC_Min_List[0]) + abs(Close_YCC_Min_List[1]) + abs(Close_YCC_Min_List[2]);

		if (Close_YCC_Dis > Close_YCC_Dis_List) {
			Close_YCC = Compare_YCC_List[i];
			Close_YCC_Index = i;
		}
		else	continue;
	}

	return Close_YCC_Index;
}

void AdaptiveYCCUpdate(vector<vector<Point>> Contours, int Close_Index) {
	Mat contours_mask = Mat::zeros(frame.size(), CV_8UC1);
	drawContours(contours_mask, Contours, Close_Index, Scalar(255), -1);
//	imshow("contours_mask", contours_mask);

	vector<Scalar> pixels;
	for (int y = 0; y < contours_mask.rows; y++) {
		for (int x = 0; x < contours_mask.cols; x++) {
			if (contours_mask.at<uchar>(y, x) == 255) {
				pixels.push_back(ycc_frame.at<Vec3b>(y, x));
			}
		}
	}

	Scalar This_YCC = pixels_avg(pixels);
//	meanYCC = (meanYCC + This_YCC) / 2;
	meanYCC = (meanYCC * 80 + This_YCC * 20) / 100;

//	cout << "This YCC : " << This_YCC << endl;
//	cout << "mean YCC : " << meanYCC << endl;
}

void YCC_Process(Scalar lowerYCC, Scalar upperYCC, Size resol) {
	Mat mask;
	vector<Scalar> YCC_List;

	inRange(ycc_frame, lowerYCC, upperYCC, mask);

	vector<vector<Point>> contours;
	findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	contours = SortingArea(contours);

	imshow("mask", mask);
//	cout << contours.size() << endl;

	if(contours.size() >= COUNT_CONTOUR){
		for (int i = 0; i < COUNT_CONTOUR; i++) {
			Rect Max_BoundRect = boundingRect(contours[i]);
			if (Max_BoundRect.width < resol.width / 20 || Max_BoundRect.height < resol.height / 20)	continue;

			Mat contours_mask = Mat::zeros(frame.size(), CV_8UC1);
			drawContours(contours_mask, contours, i, Scalar(255), -1);

			//mean으로 가능은 할 듯
			vector<Scalar> pixels;
			for (int y = 0; y < contours_mask.rows; y++) {
				for (int x = 0; x < contours_mask.cols; x++) {
					if (contours_mask.at<uchar>(y, x) == 255) {
						pixels.push_back(ycc_frame.at<Vec3b>(y, x));
					}
				}
			}

			YCC_List.push_back(pixels_avg(pixels));
		}
	}

	if (YCC_List.size() > 0) {
		int Close_Contour_Index = Close_YCC_Pro(YCC_List);
		Rect boundRect = boundingRect(contours[Close_Contour_Index]);
		rectangle(frame, boundRect, Scalar(0, 255, 0), 2);

		Adaptive_Cnt++;
		if(Adaptive_Cnt >= 5){
			AdaptiveYCCUpdate(contours, Close_Contour_Index);
			Adaptive_Cnt = 0;
		}
	}
}

Scalar Push_Button(Rect2i roi_point) {
	Mat roi_ycc_frame;
	roi_ycc_frame = frame(roi_point).clone();
	cvtColor(roi_ycc_frame, roi_ycc_frame, COLOR_BGR2YCrCb);

	return mean(roi_ycc_frame);
}

int main() {
	VideoCapture cap = VideoCapture(0);
	if (!cap.isOpened()) {
		cout << "Could't load camera" << endl;
		return -1;
	}

	double width = cap.get(CAP_PROP_FRAME_WIDTH);
	double height = cap.get(CAP_PROP_FRAME_HEIGHT);
	width = 640;
	height = 480;
	cap.set(CAP_PROP_FRAME_WIDTH, width);
	cap.set(CAP_PROP_FRAME_HEIGHT, height);

	namedWindow("frame");

	int key;
	int frameCount = 0;
	bool Start_Flag = false;
	Scalar dis_YCC = Scalar(20, 10, 10);
	Scalar lowerYCC, upperYCC;
	Rect2i roi_frame; //= Rect(Point(width / 10 * 4, height / 10 * 5), Point(width / 10 * 5, height / 10 * 6));

	Mat Y2C_frame;
	while (1) {
		cap >> frame;
		flip(frame, frame, 1);
		if (frame.empty()) {
			cout << "Could't load frame" << endl;
			return -1;
		}

		key = waitKey(1);
		if (key == 27)	break;
		else if (key == 32) {
			roi_frame = selectROI(frame, false);

			meanYCC = Push_Button(roi_frame);

			lowerYCC = meanYCC - dis_YCC;
			upperYCC = meanYCC + dis_YCC;

			Start_Flag = true;
		}

		if (Start_Flag){
			cvtColor(frame, ycc_frame, COLOR_BGR2YCrCb);
			YCC_Process(lowerYCC, upperYCC, Size(width, height));
			rectangle(frame, roi_frame, Scalar(0, 0, 255), 2, 8);
		}

//		rectangle(frame, roi_frame, Scalar(0, 0, 255), 2, 8);
		imshow("frame", frame);
	}

	cap.release();
	destroyAllWindows();

	return 0;
}