C# OpenCV Kmeans

K-means algorithm 란?

k-평균 클러스터링 알고리즘은 클러스터링 방법 중 분할법에 속한다. 분할법은 주어진 데이터를 여러 파티션 (그룹) 으로 나누는 방법이다. 예를 들어 n개의 데이터 오브젝트를 입력받았다고 가정하자. 이 때 분할법은 입력 데이터를 n보다 작거나 같은 k개의 그룹으로 나누는데, 이 때 각 군집은 클러스터를 형성하게 된다. 다시 말해, 데이터를 한 개 이상의 데이터 오브젝트로 구성된 k개의 그룹으로 나누는 것이다. 이 때 그룹을 나누는 과정은 거리 기반의 그룹간 비유사도 (dissimilarity) 와 같은 비용 함수 (cost function) 을 최소화하는 방식으로 이루어지며, 이 과정에서 같은 그룹 내 데이터 오브젝트 끼리의 유사도는 증가하고, 다른 그룹에 있는 데이터 오브젝트와의 유사도는 감소하게 된다.[7] k-평균 알고리즘은 각 그룹의 중심 (centroid)과 그룹 내의 데이터 오브젝트와의 거리의 제곱합을 비용 함수로 정하고, 이 함수값을 최소화하는 방향으로 각 데이터 오브젝트의 소속 그룹을 업데이트 해 줌으로써 클러스터링을 수행하게 된다. 출저 : 위키피디아

 

적용 이미지 예시 ( OpenCV 공식 홈페이지 )

이미지 선 성분 검출을 위해서 서전에 이 작업을 거치면 비교적 원하는 결과를 얻을 수 있다.

OpenCVSharp ver 3.2에서는 Kmeans Method를 지원한다.

이 Method의 Parameter의 의미는 여기에서 참조 하길 바란다.

아래는  C#으로 구현된 OpenCVSharp을 활용한 K-means

public static void Kmeans(Mat input, Mat result, int k)
{
    using (Mat points = new Mat())
    {
	using (Mat labels = new Mat())
	{
	    using (Mat centers = new Mat())
	    {
		int width = input.Cols;
		int height = input.Rows;

		points.Create(width * height, 1, MatType.CV_32FC3);
		centers.Create(k, 1, points.Type());
		result.Create(height, width, input.Type());

		int i = 0;
		for (int y = 0; y < height; y++)
		{
		    for (int x = 0; x < width; x++, i++)
		    {
			Vec3f vec3f = new Vec3f
			{
			    Item0 = input.At<Vec3b>(y, x).Item0,
			    Item1 = input.At<Vec3b>(y, x).Item1,
			    Item2 = input.At<Vec3b>(y, x).Item2
			};

			points.Set<Vec3f>(i, vec3f);
		    }
		}

		Cv2.Kmeans(points, k, labels, new TermCriteria(CriteriaType.Eps | CriteriaType.MaxIter, 10, 1.0), 3, KMeansFlags.PpCenters, centers);

		i = 0;
		for (int y = 0; y < height; y++)
		{
		    for (int x = 0; x < width; x++, i++)
		    {
			int idx = labels.Get<int>(i);

			Vec3b vec3b = new Vec3b();


			int tmp = Convert.ToInt32(Math.Round(centers.At<Vec3f>(idx).Item0));
			tmp = tmp > 255 ? 255 : tmp < 0 ? 0 : tmp;
			vec3b.Item0 = Convert.ToByte(tmp);

			tmp = Convert.ToInt32(Math.Round(centers.At<Vec3f>(idx).Item1));
			tmp = tmp > 255 ? 255 : tmp < 0 ? 0 : tmp;
			vec3b.Item1 = Convert.ToByte(tmp);

			tmp = Convert.ToInt32(Math.Round(centers.At<Vec3f>(idx).Item2));
			tmp = tmp > 255 ? 255 : tmp < 0 ? 0 : tmp;
			vec3b.Item2 = Convert.ToByte(tmp);

			result.Set<Vec3b>(y, x, vec3b);

		    }
		}
	    }
	}
    }

}

 

 

 

  • 2017년 12월 3일