[OpenCV] QuadTree Clustering C++ Code

2019. 11. 25. 18:36· Programming/OpenCV
반응형

2019.11.25

 

오늘은 QuadTree Clustering 알고리즘을 설명합니다. QuadTree 알고리즘은 상당히 많은 곳에서 사용되고 있습니다. QuadTree 알고리즘은 여러 장점이 있는데 그 중에서도 Clustering 하는데에 속도가 매우 빠르다는 장점이 유용한 경우가 많습니다. 구두로 간단히 QuadTree Clustering을 설명하자면, 알고리즘 이름에서 알다시피 네 개의 자식을 가지는 tree 구조로 되어있습니다. 이 것을 칼라 이미지에만 적용하여 설명하자면, 입력 이미지를 Root 노드로 하여 이미지를 네 등분하여 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래 순서로 분할된 영역을 데이터로 가지는 자식을 생성합니다. 이미지의 생성된 자식 노드에 해당하는 영역의 화소값들의 평균을 구합니다. 그리고 해당 영역의 모든 픽셀들과 그 평균값과의 차이가 임계값보다 적으면 그 영역은 비슷한 색상을 가지고 있다고 판단하여 Clustering이 되며 그 영역의 평균값이 그 영역의 픽셀값이 됩니다. 그리고 임계값보다 크다면 다시 그 영역을 부모 노드로 하여 네 등분하는 영역을 자식 노드로 취하게 됩니다. 이렇게 반복을 진행하는데 분할된 영역의 넓이가 어느 임계값보다 작아지기전까지 분할을 하게 됩니다. 이렇게 되면 전체 알고리즘에 필요한 하이퍼 파라미터는 두 개입니다. 픽셀들과 평균값의 차이를 결정짓는 임계값1과 영역의 쪼개지는 넓이의 최소점을 의미하는 임계값2 입니다. 앞서 포스팅한 K-means Clustering과 비교해보면 분할하는 사각형의 넓이를 충분히 작게하면 훨씬 더 보기 좋은 결과 이미지를 내보내게 되고 비교도 안될정도로 빠른 속도로 Clustering을 진행합니다. 두 알고리즘의 차이는 Global하다는 것과 Local하다는 것의 차이가 있습니다. 아래는 C++로 구현된 QuadTree Code 입니다.

 

main.cpp

#include <iostream>
#include <vector>
#include <ctime>

#include "QuadTree.h"
#include "opencv342.h"

using namespace std;

cv::Mat QuadTree_Cluster(cv::Mat, int minArea, float threshold);

int main()
{
	// Input Image의 파일명
	string CLUSTER[5] = { "12003.jpg", "25098.jpg", "97017.jpg", "151087.jpg", "181018.jpg" };
	string path = "testImage/";

	// QuadTree Clustering
	for (int i = 0; i < 5; i++) {
		cv::Mat img = cv::imread(path + CLUSTER[i]);
		cv::Mat result;
		int minArea = 10;
		float threshold = 5.0;

		result = QuadTree_Cluster(img, minArea, threshold); // src, minArea, threshold
		cv::imwrite(path + "Result_Quad/" + CLUSTER[i], result);
	}


	return 0;
}

cv::Mat QuadTree_Cluster(cv::Mat src, int minArea, float threshold) {
	cv::Mat result;

	QuadTree clustering(src, minArea, threshold);

	result = clustering.getResultMat();

	return result;
}

 

QuadTree.h

#pragma once
#include <iostream>

#include "opencv342.h"

using namespace std;

class QuadTree
{
public:
	QuadTree(cv::Mat src, int area, float threshold);
	~QuadTree();

	cv::Mat getResultMat();
	
private:
	cv::Mat img;
	cv::Mat dst;

	uchar* sImage;
	uchar* dImage;
	int minArea;
	float thr;

	struct Node {
		int x, y, width, height;
		Node* Children;
	};

	Node root;

private:
	cv::Point3f average(int x, int y, int width, int height);
	float measureDetail(int x, int y, int width, int height);
	void createQuadTree(Node node);
	void divideNode(Node& node);
	void coloring(cv::Point3f avg, int x, int y, int width, int height);
	void TestShowImage(int x, int y, int width, int height);
};

QuadTree.cpp

#include "QuadTree.h"

QuadTree::QuadTree(cv::Mat src, int area, float threshold)
	:img(src), minArea(area), thr(threshold)
{
	dst = cv::Mat::zeros(img.size(), CV_8UC3);

	sImage = img.data;
	dImage = dst.data;

	root.x = 0;
	root.y = 0;
	root.width = img.cols;
	root.height = img.rows;

	createQuadTree(root);

	//cv::imshow("Test from QuadTree", img);
	//cv::waitKey(0);
}

QuadTree::~QuadTree() {
	delete root.Children;
}

/* Calculates the average color of a rectangular region of a grid */
cv::Point3f QuadTree::average(int x, int y, int width, int height) {	
	int redSum = 0;
	int greenSum = 0;
	int blueSum = 0;

	// Adds the color values for each channel.
	for (int r = y; r < y + height; r++) {
		for (int c = x; c < x + width; c++) {
			int offset = r * img.cols * 3 + c * 3;
			
			uchar B = sImage[offset + 0]; //img.at<cv::Vec3b>(r, c)[0];
			uchar G = sImage[offset + 1]; //img.at<cv::Vec3b>(r, c)[1];
			uchar R = sImage[offset + 2]; //img.at<cv::Vec3b>(r, c)[2];

			blueSum += B;
			greenSum += G;
			redSum += R;
		}
	}

	// number of pixels evaluated
	int area = width * height;
	return cv::Point3f(blueSum / area, greenSum / area, redSum / area);
}

/* Measures the amount of detail of a rectangular region of a grid */
float QuadTree::measureDetail(int x, int y, int width, int height) {
	cv::Point3f averageColor = average(x, y, width, height);
	
	int blue = averageColor.x;
	int red = averageColor.z;
	int green = averageColor.y;	
	int colorSum = 0;

	// Calculate the distance between every pixel in the region
	// and the average color. The Manhattan distance is used, and
	// all the distances are added.
	for (int r = y; r < y + height; r++) {
		for (int c = x; c < x + width; c++) {
			int offset = r * img.cols * 3 + c * 3;

			uchar B = sImage[offset + 0];
			uchar G = sImage[offset + 1];
			uchar R = sImage[offset + 2];

			colorSum += abs(blue - B);
			colorSum += abs(green - G);
			colorSum += abs(red - R);
		}
	}
	// Calculates the average distance, and returns the result.
	// We divide by three, because we are averaging over 3 channels.
	int area = width * height;
	return colorSum / (3 * area);
}

void QuadTree::createQuadTree(Node node) {
	if (measureDetail(node.x, node.y, node.width, node.height) < thr || node.width * node.height <= minArea) {
		cv::Point3i avgBGR = average(node.x, node.y, node.width, node.height);
		coloring(avgBGR, node.x, node.y, node.width, node.height);
	}
	else {
		//TestShowImage(node.x, node.y, node.width, node.height);
		divideNode(node);
		createQuadTree(node.Children[0]);
		createQuadTree(node.Children[1]);
		createQuadTree(node.Children[2]);
		createQuadTree(node.Children[3]);
	}
}

void QuadTree::divideNode(Node& node) {
	// Node division
	node.Children = new Node[4];

	int x = node.x;
	int y = node.y;
	int width = node.width;
	int height = node.height;

	node.Children[0].x = x;
	node.Children[0].y = y;
	node.Children[0].width = width / 2;
	node.Children[0].height = height / 2;

	node.Children[1].x = x + width / 2;
	node.Children[1].y = y;
	node.Children[1].width = width - width / 2;
	node.Children[1].height = height / 2;

	node.Children[2].x = x;
	node.Children[2].y = y + height / 2;
	node.Children[2].width = width / 2;
	node.Children[2].height = height - height / 2;
	
	node.Children[3].x = x + width / 2;
	node.Children[3].y = y + height / 2;
	node.Children[3].width = width - width / 2;
	node.Children[3].height = height - height / 2;
}

// Coloring average bgr value in destination image.
void QuadTree::coloring(cv::Point3f avg, int x, int y, int width, int height) {
	uchar* dImage = dst.data;
	for (int r = y; r < y + height; r++) {
		for (int c = x; c < x + width; c++) {
			int offset = r * img.cols * 3 + c * 3;
			dImage[offset + 0] = avg.x;
			dImage[offset + 1] = avg.y;
			dImage[offset + 2] = avg.z;
		}
	}
}

void QuadTree::TestShowImage(int x, int y, int width, int height) {
	cv::imshow("Test", img(cv::Rect(x, y, width, height)));
	cv::waitKey(0);
}
cv::Mat QuadTree::getResultMat() {
	return dst;
}

 

 

반응형

'Programming > OpenCV' 카테고리의 다른 글

[OpenCV] MeanShift Clustering C++ Code  (0) 2020.05.13
[OpenCV] KMeans Clustering C++ Code  (0) 2019.11.25
[OpenCV] Otsu's Algorithm 이론 설명 및 C++ Code  (0) 2019.04.05
'Programming/OpenCV' 카테고리의 다른 글
  • [OpenCV] MeanShift Clustering C++ Code
  • [OpenCV] KMeans Clustering C++ Code
  • [OpenCV] Otsu's Algorithm 이론 설명 및 C++ Code
오뚜깅
오뚜깅
오뚜깅
오뚜깅
오뚜깅
전체
오늘
어제
  • 분류 전체보기
    • 취업인생
    • Programming
      • C & C++
      • Python
      • OpenCV
      • PCL
      • ROS
      • Deep learning
      • Network
    • 알고리즘
      • 이론
      • 백준(BOJ)
      • 프로그래머스(Programmers)
    • Project
    • IT
      • 우분투
    • 일상
      • 말씀 묵상
      • 끄적임
      • 영어 일기

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • rospy
  • installcudnn
  • 오츠알고리즘
  • 딥러닝환경구축
  • graphicdriver
  • 우분투
  • DeepLearning
  • 백준2798
  • 사용자지정정규화공식
  • opencv
  • cv_bridge
  • clustering
  • CUDA
  • CuDNN
  • tensorflowversion
  • PointCloudLibrary
  • OtsuAlgorithm
  • C++
  • kmeansclustering
  • 2292
  • imageclustering
  • pointcloud
  • installubuntu
  • 백준2231
  • pytorch
  • installcuda
  • cuda9.0
  • cudaversion
  • c++code
  • cuda설치

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
오뚜깅
[OpenCV] QuadTree Clustering C++ Code
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.