[ONVIF] C# Onvif.Core

2026. 3. 23. 00:00

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

반응형

마우스로 마스크 포인트의 삽입, 삭제, 이동을 구현해 보자.

 

#include <iostream>
#include <opencv2/opencv.hpp>

cv::Point nearPointOnSegment(cv::Point2f p, cv::Point2f a, cv::Point2f b) {
	float l2 = std::powf(a.x - b.x, 2) + std::powf(a.y - b.y, 2); // 선분의 길이 제곱
	if (l2 == 0.0f) // a와 b가 같은 경우
		return cv::Point();

	// 선분 위로 점 p의 사영(Projection) 위치 t 계산(투영된 지점의 비율)
	// t = [(p-a) dot (b-a)] / |b-a|^2
	float t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / l2;

	// t가 0~1 사이면 선분 위(0이면 a, 1이면 b), 그렇지 않으면 선분 밖
	// 0보다 작으면 a쪽, 1보다 크면 b쪽
	// 선분 밖에 사영된 점은 무시
	if (t < 0.0f || t > 1.0f)
		return cv::Point();

	// 선분 위에 사영된 점 위치 계산
	cv::Point2f projection = a + t * (b - a);
	//std::cout << "Projection Point: (" << projection.x << ", " << projection.y << ")" << std::endl;

	// 사영된 점과 원래 점 p 사이의 거리가 10 픽셀 이상이면 무시
	if (std::sqrt(std::powf(p.x - projection.x, 2) + std::powf(p.y - projection.y, 2)) > 10)
		return cv::Point();

	// 선분 위의 사영된 점 반환
	return projection;
}

void onMouse(int event, int x, int y, int flags, void* userdata)
{
	static std::vector<cv::Point> points; // Static vector to store points across function calls
	cv::Scalar pointColor(0, 255, 0); // Green color for drawing points
	cv::Scalar lineColor(0, 0, 255); // Red color for drawing lines
	static cv::Mat* img = (cv::Mat*)userdata; // Cast the user data to a pointer to cv::Mat

	// 이미지 복사본 생성
	static cv::Mat originalimg = cv::imread("palvin1.png");

	// 마스크 생성
	static cv::Mat mask1 = cv::Mat::zeros((*img).size(), CV_8UC1);
	static cv::Mat mask2 = cv::Mat::zeros((*img).size(), CV_8UC1);
	static cv::Mat mask = cv::Mat::zeros((*img).size(), CV_8UC1);

	cv::Point newPoint(x, y);

	static bool clickedOnExistingPoint = false; // Flag to track if the click was on an existing point
	static int clickedExistingPointIndex = -1; // Index of the existing point that was clicked
	static bool clickHold = false;

	bool inserted = false;

	switch (event)
	{
	case cv::EVENT_LBUTTONDOWN:
		// 기존 점과 동일한 위치에 클릭된 경우 (범위는 8x8 픽셀)
		if (!points.empty())
			for (int i = 0; i < points.size(); i++)
				if (newPoint.x - 4 < points[i].x && newPoint.x + 4 > points[i].x &&
					newPoint.y - 4 < points[i].y && newPoint.y + 4 > points[i].y)
				{
					// 이미 존재하는 점을 클릭하면 우클릭으로 제거할 수 있도록 인덱스 저장
					clickedExistingPointIndex = i;
					clickedOnExistingPoint = true; // Set the flag to indicate that an existing point was clicked

					// 이동하려는 목적일 수 있으므로, 클릭이 유지되는 동안 점을 이동할 수 있도록 clickHold 플래그 설정
					clickHold = true;

					std::cout << "Clicked on existing point: " << i << " (" << points[i].x << ", "
						<< points[i].y << ")" << std::endl;

					return;
				}


		// 마우스 왼쪽 버튼이 클릭되었을 때, 새로운 점을 추가하거나 선분 사이에 점을 삽입

		// 선분 사이에 점 추가
		// 최소 2개의 점이 있어야 선분이 형성되므로, 2개 이상의 점이 있을 때만 선분 사이에 점을 추가
		if (points.size() >= 2)
		{
			for (int i = 0; i < points.size() - 1; i++)
			{
				cv::Point2f a = points[i];
				cv::Point2f b = points[i + 1];
				cv::Point projection = nearPointOnSegment(newPoint, a, b);
				if (projection == cv::Point()) // 사영된 점이 선분 밖이거나 너무 멀리 떨어져 있으면
					continue;
				else
				{
					points.insert(points.begin() + 1 + i, projection); // 새로운 점을 선분 사이에 삽입
					std::cout << "Point inserted: (" << projection.x << ", " << projection.y << ")" << std::endl;

					originalimg.copyTo(*img); // 이미지 갱신
					inserted = true;

					break;
				}
			}
		}

		// 선분 사이에 점이 추가되지 않았다면, 새로운 점을 추가
		if (!inserted && !points.empty() && newPoint != points.back())
		{
			points.push_back(newPoint);
			std::cout << "Point added: (" << x << ", " << y << ")" << std::endl;

			// 이전에 클릭된 점에 대한 정보를 리셋, 삭제시 전에 클릭된 점이 삭제되지 않도록 한다
			// 이 부분이 없으면, 이미 존재하던 점을 클릭한 후 다른 곳을 클릭해 새로운 점을 생성하고 우클릭으로 삭제하면,
			// 새로운 점이 삭제되지 않고 이전에 클릭된 점이 삭제되는 문제가 발생한다
			clickedExistingPointIndex = -1;
			clickedOnExistingPoint = false;
		}
		// 첫 번째 점 추가
		else if (points.empty())
		{
			points.push_back(newPoint);
			std::cout << "First point added: (" << x << ", " << y << ")" << std::endl;
		}

		break;

	case cv::EVENT_LBUTTONUP:
		// 클릭이 끝났으므로, 클릭이 유지되는 동안 점을 이동할 수 있도록 설정한 clickHold 플래그를 false로 리셋
		clickHold = false;

		break;

	case cv::EVENT_MOUSEMOVE:
		// 클릭이 유지되는 동안 점을 이동할 수 있도록 설정한 clickHold 플래그가 true인 경우
		if (clickHold && clickedOnExistingPoint && clickedExistingPointIndex != -1)
		{
			points[clickedExistingPointIndex] = newPoint; // Move the existing point to the new location
			//std::cout << "Moving point: " << clickedExistingPointIndex << " (" << x << ", " << y << ")" << std::endl;

			originalimg.copyTo(*img, mask); // 마스크를 적용하여 원본 이미지에서 점과 선이 있는 부분만 갱신
			// *img = cv::imread("palvin1.png");
			// 이런 식으로 매번 이미지를 다시 로드하는게 오래 걸려서 점 이동시 버벅이는거 같아 마스크를 적용해
			// 원본 이미지에서 복사해 봤지만 별 효과는 없다.
			// cv::imshow()가 이런 작업에 최적화되어 있지 않은 것 같기도 하다. 점 이동시 버벅이는 문제는 해결되지 않았다.
		}
		break;

	case cv::EVENT_RBUTTONDOWN:
		if (!points.empty())
		{
			// 점이 삽입된 상태에서 우클릭이 발생하면 삽입된 점을 제거, 또는 기존 점을 클릭한 상태에서 우클릭이 발생하면
			// 클릭된 점을 제거
			if (clickedOnExistingPoint)
			{
				std::cout << "Clicked point removed: " << clickedExistingPointIndex << " (" <<
					points[clickedExistingPointIndex].x << ", " << points[clickedExistingPointIndex].y << ")" <<std::endl;
				points.erase(points.begin() + clickedExistingPointIndex); // Remove the last added point
				clickedExistingPointIndex = -1; // Reset the last added point index
				clickedOnExistingPoint = false; // Reset the added flag
			}
			// 아니면 마지막으로 추가된 점을 제거
			else
			{
				points.pop_back();
				std::cout << "Last point removed" << std::endl;
			}

			originalimg.copyTo(*img); // 이미지 갱신
		}
		break;
	}

	// Draw points
	for (const cv::Point& point : points)
		cv::circle(*(cv::Mat*)userdata, point, 4, pointColor, -1); // userdata로도 접근 가능

	// Draw lines between points
	if (points.size() >= 2)
		for (int i = 0; i < points.size() - 1; i++)
			cv::line(*img, points[i], points[i + 1], lineColor, 2);

	// 빨간색 점과 초록색 선분이 있는 부분을 흰색으로, 나머지 부분을 검은색으로 하는 마스크 생성
	cv::inRange(*img, cv::Scalar(0, 0, 255), cv::Scalar(0, 0, 255), mask1);
	cv::inRange(*img, cv::Scalar(0, 255, 0), cv::Scalar(0, 255, 0), mask2);
	mask = mask1 + mask2;

	//cv::imshow("mask", mask);
	//cv::imshow("originalimg", originalimg);
	cv::imshow("image", *(cv::Mat*)userdata);
}

int main()
{
	cv::Mat image = cv::imread("palvin1.png");
	cv::namedWindow("image");
	cv::setMouseCallback("image", onMouse, (void*)&image);

	cv::imshow("image", image);

	cv::waitKey(0);
	cv::destroyAllWindows();

	return 0;
}

 

 

 

 

 

 

 

반응형
Posted by J-sean
:
반응형

두 점으로 이루어지는 선분과 한 점 사이의 거리 및 수선의 발을 찾아보자.

 

#include <opencv2/opencv.hpp>
#include <iostream>

float distPointToSegment(cv::Point2f p, cv::Point2f a, cv::Point2f b) {
	float l2 = std::powf(a.x - b.x, 2) + std::powf(a.y - b.y, 2); // 선분의 길이 제곱
	if (l2 == 0.0f) // a와 b가 같은 경우
		return std::sqrt(std::powf(p.x - a.x, 2) + std::powf(p.y - a.y, 2)); // 점과 a 사이 거리

	// 선분 위로 점 p의 사영(Projection) 위치 t 계산(투영된 지점의 비율)
	// t = [(p-a) dot (b-a)] / |b-a|^2
	float t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / l2;

	// t가 0~1 사이면 선분 위(0이면 a, 1이면 b), 그렇지 않으면 선분 밖
	// 0보다 작으면 a쪽, 1보다 크면 b쪽
	if (t < 0.0f)
		return std::sqrt(std::powf(p.x - a.x, 2) + std::powf(p.y - a.y, 2));
	if (t > 1.0f)
		return std::sqrt(std::powf(p.x - b.x, 2) + std::powf(p.y - b.y, 2));

	// 선분 위에 사영된 점 위치 계산
	cv::Point2f projection = a + t * (b - a);
	std::cout << "Projection Point: (" << projection.x << ", " << projection.y << ")" << std::endl;

	// 점 p와 사영된 점 사이의 거리 계산
	return std::sqrt(std::powf(p.x - projection.x, 2) + std::powf(p.y - projection.y, 2));
}

int main() {
	cv::Point2f p(100, 0); // 점 p
	cv::Point2f a(0, 0);   // 선분 시작점 a
	cv::Point2f b(100, 100); // 선분 끝점 b

	float distance = distPointToSegment(p, a, b);
	std::cout << "Point to Segment Distance: " << distance << std::endl;

	return 0;
}

 

 

반응형
Posted by J-sean
:
반응형

라인 위의 모든 점의 좌표를 찾아보자.

 

#include <iostream>
#include <opencv2/opencv.hpp>

int main() {
	cv::Mat img = cv::Mat::zeros(480, 640, CV_8UC3);
	cv::Point p1(100, 100);
	cv::Point p2(500, 300);

	// LineIterator 생성 (이미지, 시작점, 끝점, 8-connectivity)
	cv::LineIterator it(img, p1, p2, 8);
	// connectivity: Pixel connectivity of the iterator.
	// Valid values are 4 (iterator can move up, down, left and right)
	// and 8 (iterator can also move diagonally).
	//
	// The number of pixels along the line is stored in LineIterator::count.
	// The method LineIterator::pos returns the current position in the image

	std::vector<cv::Point> linePoints;
	for (int i = 0; i < it.count; i++, ++it) {
		// 현재 위치의 좌표를 벡터에 저장
		linePoints.push_back(it.pos());

		// 선 그리기
		img.at<cv::Vec3b>(it.pos()) = cv::Vec3b(0, 255, 0); // 초록색
	}

	// cv::norm 함수는 벡터의 크기를 계산하는 함수로, 두 점 사이의 거리를 계산할 때 사용할 수 있다.
	//double distance = cv::norm(p2 - p1);
	// 또는 cv::sqrt 함수를 사용하여 직접 계산할 수도 있다.
	cv::Point2f diff = p2 - p1;
	double distance = cv::sqrt(diff.x * diff.x + diff.y * diff.y);
	std::cout << "p1과 p2 사이의 거리: " << distance << std::endl;

	std::cout << "라인 위의 포인트 개수: " << linePoints.size() << std::endl;
	std::cout << linePoints << std::endl;

	cv::imshow("Line", img);
	cv::waitKey(0);

	return 0;
}

 

 

 

※ 참고

LineIterator Class

 

반응형
Posted by J-sean
:

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

반응형

폴리곤 마스크를 생성하고 사용해 보자.

 

#include <iostream>
#include <opencv2/opencv.hpp>

void onMouse(int event, int x, int y, int flags, void* userdata)
{
	static std::vector<cv::Point> points; // Static vector to store points across function calls
	cv::Scalar color(0, 255, 0); // Green color for drawing
	cv::Mat* img = (cv::Mat*)userdata; // Cast the user data to a pointer to cv::Mat

	switch (event)
	{
	case cv::EVENT_LBUTTONDOWN:
		points.push_back(cv::Point(x, y));
		std::cout << "Point added: (" << x << ", " << y << ")" << std::endl;

		break;

	case cv::EVENT_RBUTTONDOWN:
		if (!points.empty())
		{
			points.pop_back(); // Remove the last point if right button is clicked
			std::cout << "Last point removed" << std::endl;
			*img = cv::imread("palvin1.png"); // Reload the original image to clear drawings
		}
		break;
	}

	// Draw points
	for (const cv::Point& point : points)
		cv::circle(*(cv::Mat*)userdata, point, 5, color, -1);
	// userdata로도 접근 가능하지만, img 포인터로도 접근 가능

	// Draw lines between points
	if (points.size() >= 2)
		for (int i = 0; i < points.size() - 1; i++)
			cv::line(*img, points[i], points[i + 1], cv::Scalar(0, 0, 255), 2);

	cv::imshow("image", *(cv::Mat*)userdata);
}

int main()
{
	cv::Mat image = cv::imread("palvin1.png");
	cv::namedWindow("image");
	cv::setMouseCallback("image", onMouse, (void*)&image);

	cv::imshow("image", image);

	cv::waitKey(0);
	cv::destroyAllWindows();

	return 0;
}

 

 

 

 

#include <iostream>
#include <opencv2/opencv.hpp>

void onMouse(int event, int x, int y, int flags, void* userdata)
{
	static std::vector<std::pair<cv::Point, cv::Scalar>> points; // Store points along with their colors
	cv::Scalar color(0, 255, 0); // Default color is green
	cv::Mat* img = (cv::Mat*)userdata; // Cast the user data to a pointer to cv::Mat

	switch (event)
	{
	case cv::EVENT_LBUTTONDOWN:
		if (flags & cv::EVENT_FLAG_CTRLKEY) // Check if Ctrl key is pressed
			color = cv::Scalar(255, 0, 0); // Blue color if Ctrl is pressed

		points.push_back(std::make_pair(cv::Point(x, y), color));
		std::cout << "Point added: (" << x << ", " << y << ") with color "
			<< (color == cv::Scalar(0, 255, 0) ? "Green" : "Blue") << std::endl;

		break;

	case cv::EVENT_RBUTTONDOWN:
		if (!points.empty())
		{
			points.pop_back(); // Remove the last point if right button is clicked
			std::cout << "Last point removed" << std::endl;
			*img = cv::imread("palvin1.png"); // Reload the original image to clear drawings
		}
		break;
	}

	// Draw points
	for (const std::pair<cv::Point, cv::Scalar>& point_color_pair : points)
		cv::circle(*(cv::Mat*)userdata, point_color_pair.first, 5, point_color_pair.second, -1);
	// userdata로도 접근 가능하지만, img 포인터로도 접근 가능

	// Draw lines between points
	if (points.size() >= 2)
		for (int i = 0; i < points.size() - 1; i++)
			cv::line(*img, points[i].first, points[i + 1].first, cv::Scalar(0, 0, 255), 2);

	cv::imshow("image", *(cv::Mat*)userdata);
}

int main()
{
	cv::Mat image = cv::imread("palvin1.png");
	cv::namedWindow("image");
	cv::setMouseCallback("image", onMouse, (void*)&image);

	cv::imshow("image", image);

	cv::waitKey(0);
	cv::destroyAllWindows();

	return 0;
}

 

 

 

 

#include <iostream>
#include <opencv2/opencv.hpp>

std::vector<cv::Point> points;

void onMouse(int event, int x, int y, int flags, void* userdata)
{
	cv::Scalar color(0, 255, 0); // Green color for drawing
	cv::Mat* img = (cv::Mat*)userdata; // Cast the user data to a pointer to cv::Mat

	switch (event)
	{
	case cv::EVENT_LBUTTONDOWN:
		points.push_back(cv::Point(x, y));
		std::cout << "Point added: (" << x << ", " << y << ")" << std::endl;

		break;

	case cv::EVENT_RBUTTONDOWN:
		if (!points.empty())
		{
			points.pop_back(); // Remove the last point if right button is clicked
			std::cout << "Last point removed" << std::endl;
			*img = cv::imread("palvin1.png"); // Reload the original image to clear drawings
		}
		break;
	}

	// Draw points
	for (const cv::Point& point : points)
		cv::circle(*(cv::Mat*)userdata, point, 5, color, -1);
	// userdata로도 접근 가능하지만, img 포인터로도 접근 가능

	// Draw lines between points
	if (points.size() >= 2)
		for (int i = 0; i < points.size() - 1; i++)
			cv::line(*img, points[i], points[i + 1], cv::Scalar(0, 0, 255), 2);

	cv::imshow("image", *(cv::Mat*)userdata);
}

int main()
{
	cv::Mat image = cv::imread("palvin1.png");
	cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1);
	cv::Mat result = cv::Mat::zeros(image.size(), image.type());
	cv::Mat roi;

	cv::Rect rect;

	cv::namedWindow("image");
	cv::setMouseCallback("image", onMouse, (void*)&image);

	cv::imshow("image", image);

	while (true)
	{
		int key = cv::waitKey(10);
		if (key == 27) // ESC key
			break;

		switch (key)
		{
			// c키를 누르면 모든 점을 지우고 원본 이미지를 다시 불러와서 초기화
		case 'c':
			points.clear(); // Clear all points if 'c' key is pressed
			image = cv::imread("palvin1.png"); // Reload the original image to clear drawings

			mask.setTo(0); // Clear the mask as well
			cv::imshow("image", image);
			if (cv::getWindowProperty("mask", cv::WND_PROP_VISIBLE) == 1) // Check if the mask window is open
				cv::imshow("mask", mask); // Clear the mask display as well

			result.setTo(0); // Clear the result image as well
			if (cv::getWindowProperty("result", cv::WND_PROP_VISIBLE) == 1) // Check if the result window is open
				cv::imshow("result", result); // Clear the result display as well

			roi.setTo(0); // Clear the ROI image as well
			if (cv::getWindowProperty("ROI", cv::WND_PROP_VISIBLE) == 1) // Check if the ROI window is open
				cv::imshow("ROI", roi); // Clear the ROI display as well

			break;

			// m키를 누르면 points 벡터에 저장된 점들을 이용하여 mask 이미지를 채우고, mask 이미지를 화면에 표시
		case 'm':
			mask.setTo(0); // Clear the mask before filling
			cv::fillPoly(mask, points, cv::Scalar(255)); // Fill the polygon defined by points
			cv::imshow("mask", mask);

			break;

			// r키를 누르면 mask 이미지를 이용하여 원본 이미지에서 해당 영역을 추출하여 result 이미지에 저장하고, result 이미지를 화면에 표시
		case 'r':
			image = cv::imread("palvin1.png"); // Reload the original image to clear drawings
			result.setTo(0); // Clear the result image before copying
			image.copyTo(result, mask); // Copy the masked area to the result image
			//cv::bitwise_and(image, image, result, mask); // Apply the mask to the image
			cv::imshow("result", result);

			break;

		case 'i':
			rect = cv::boundingRect(points); // Get the bounding rectangle of the points			
			roi = result(rect);
			cv::imshow("ROI", roi);

			break;

		default:
			break;
		}
	}

	cv::destroyAllWindows();

	return 0;
}

 

 

 

 

 

※ 참고

2026.03.13 - [분류 전체보기] - [OpenCV] Select Region of Interest ROI 선택

2026.03.11 - [OpenCV] - [OpenCV] Polygon Mask 폴리곤 마스크 1

 

반응형
Posted by J-sean
: