이 튜토리얼에서는 컴퓨터 비전에서 널리 사용되는 색상 공간에 대해 알아보고, 이를 색상 기반 분할에 활용해 보겠습니다. 또한 C++와 Python으로 작성된 데모 코드도 공유합니다.
1975년, 헝가리 특허 HU170062는 43,252,003,274,489,856,000(43경)의 가능성 중 단 하나의 정답만을 찾는 퍼즐을 선보였습니다. 현재 루빅큐브로 알려진 이 발명품은 2009년 1월까지 3억 5천만 개 이상 판매되며 전 세계를 휩쓸었습니다.

며칠 전 친구 마크가 컴퓨터 비전 기반 자동 루빅큐브 풀이기를 만들겠다는 아이디어를 냈을 때 정말 흥미로웠습니다. 마크는 색상 분할을 사용하여 큐브의 현재 상태를 알아내려고 했는데, 그의 색상 분할 코드는 방 안에서는 저녁 시간에는 꽤 잘 작동했지만, 방 밖에서는 낮에 제대로 작동하지 않았습니다!
그는 제게 도움을 요청했고, 저는 그가 어디에서 잘못했는지 바로 이해했습니다. 다른 많은 아마추어 컴퓨터 비전 애호가들처럼, 그는 색상 분할을 할 때 다양한 조명 조건의 영향을 고려하지 않았습니다. 피부색 감지, 신호등 인식 등 색상 기반 분할을 사용하는 많은 컴퓨터 비전 애플리케이션에서 이러한 문제가 발생합니다. 그의 로봇을 위한 강력한 색상 감지 시스템을 구축하는 데 어떻게 도움을 줄 수 있는지 살펴보겠습니다.
이 기사는 다음과 같이 구성되어 있습니다.
- 먼저 OpenCV에서 이미지를 읽고 이를 다양한 색상 공간으로 변환하는 방법을 살펴보고, 각 색상 공간의 다양한 채널이 어떤 새로운 정보를 제공하는지 살펴보겠습니다.
- 우리는 마크가 한 간단한 색상 분할 알고리즘을 적용하고 그 약점을 생각해 보겠습니다.
- 그런 다음 분석에 들어가 체계적인 방법을 사용하여 다음을 선택합니다.
- 올바른 색상 공간.
- 세분화를 위한 올바른 임계값.
- 결과를 확인하세요
다양한 색상 공간
이 섹션에서는 컴퓨터 비전에 사용되는 몇 가지 중요한 색 공간에 대해 살펴보겠습니다. 위키피디아에서 찾을 수 있으므로, 색 공간의 이론은 설명하지 않겠습니다. 대신, 기본적인 직관을 키우고 나중에 의사 결정에 유용한 몇 가지 중요한 속성을 알아보겠습니다.
같은 정육면체의 두 이미지를 불러와 보겠습니다. 기본적으로 BGR 형식으로 불러와집니다. 나중에 설명하겠지만 OpenCV 함수 cvtColor() 를 사용하여 서로 다른 색공간 간에 변환할 수 있습니다.
#python
bright = cv2.imread('cube1.jpg')
dark = cv2.imread('cube8.jpg')
//C++
bright = cv::imread('cube1.jpg')
dark = cv::imread('cube8.jpg')
첫 번째 이미지는 밝은 햇빛이 비치는 실외에서 촬영한 것이고, 두 번째 이미지는 일반적인 조명 조건의 실내에서 촬영한 것입니다.
다양한 조명 하에 있는 두 개의 큐브

그림 1 : 서로 다른 조명에서 촬영한 동일한 큐브의 두 이미지
RGB 색상 공간
RGB 색상 공간은 다음과 같은 속성을 갖습니다.
- 이는 빨간색, 녹색, 파란색 값의 선형 조합을 통해 색상을 얻는 가산 색 공간 입니다.
- 세 개의 채널은 표면에 닿는 빛의 양에 따라 상관관계가 있습니다.
두 이미지를 R, G, B 구성 요소로 분할하고 이를 관찰하여 색상 공간에 대한 더 자세한 통찰력을 얻어 보겠습니다.

그림 2 : RGB 색상 공간의 각 채널(파란색(B), 녹색(G), 빨간색(R))이 별도로 표시됨
관찰
파란색 채널을 살펴보면, 실내 조명 조건에서 두 번째 이미지에서는 파란색과 흰색 조각이 비슷하게 보이지만, 첫 번째 이미지에서는 뚜렷한 차이가 있음을 알 수 있습니다. 이러한 불균일성으로 인해 이 색 공간에서 색상 기반 분할이 매우 어렵습니다. 더 나아가, 두 이미지의 값 사이에는 전반적인 차이가 있습니다. 아래에서는 RGB 색 공간과 관련된 고유한 문제점을 요약했습니다.
- 심각한 지각적 불균일성.
- 색차(색상 관련 정보)와 휘도(강도 관련 정보) 데이터를 혼합합니다.
LAB 색상 공간
Lab 색상 공간에는 세 가지 구성 요소가 있습니다.
- L – 밝기(강도).
- a – 녹색부터 자홍색까지의 색상 구성 요소입니다.
- b – 파란색부터 노란색까지의 색상 구성 요소입니다.
Lab 색 공간은 RGB 색 공간과 상당히 다릅니다. RGB 색 공간에서는 색상 정보가 세 개의 채널로 구분되지만, 이 세 채널은 밝기 정보도 함께 인코딩합니다. 반면, Lab 색 공간에서는 L 채널이 색상 정보와는 별개로 밝기 정보만 인코딩합니다. 나머지 두 채널은 색상을 인코딩합니다.
다음과 같은 특성이 있습니다.
- 우리가 색상을 인지하는 방식에 근접한, 지각적으로 균일한 색상 공간입니다.
- 장치에 구애받지 않음(캡처나 표시).
- Adobe Photoshop에서 널리 사용됩니다.
- 복잡한 변환 방정식을 통해 RGB 색상 공간과 관련됩니다.
Lab 색상 공간에서 두 이미지를 세 개의 채널로 분리해 보겠습니다.
#python
brightLAB = cv2.cvtColor(bright, cv2.COLOR_BGR2LAB)
darkLAB = cv2.cvtColor(dark, cv2.COLOR_BGR2LAB)
//C++
cv::cvtColor(bright, brightLAB, cv::COLOR_BGR2LAB);
cv::cvtColor(dark, darkLAB, cv::COLOR_BGR2LAB);
LAB 구성 요소

그림 3: LAB 색상 공간의 명도(L) 및 색상 구성 요소(A, B).
관찰
- 그림에서 알 수 있듯이 조도의 변화는 주로 L 구성요소에 영향을 미쳤습니다.
- 색상 정보를 담고 있는 A와 B 구성요소는 큰 변화를 겪지 않았습니다.
- A 구성요소의 극단인 녹색, 주황색, 빨간색의 각각의 값은 B 구성요소에서 변경되지 않았으며 마찬가지로 B 구성요소의 극단인 파란색과 노란색의 각각의 값도 A 구성요소에서 변경되지 않았습니다.
YCrCb 색상 공간
YCrCb 색 공간은 RGB 색 공간에서 파생되었으며 다음의 세 가지 구성 요소를 갖습니다.
- Y – 감마 보정 후 RGB에서 얻은 휘도 또는 루마 구성 요소입니다.
- Cr = R – Y (빨간색 구성 요소가 루마로부터 얼마나 떨어져 있는지).
- Cb = B – Y (푸른색 구성 요소가 루마로부터 얼마나 떨어져 있는지).
이 색 공간은 다음과 같은 속성을 갖습니다.
- 휘도 및 색차 구성요소를 여러 채널로 분리합니다.
- 주로 TV 전송을 위한 압축(Cr 및 Cb 구성 요소)에 사용됩니다.
- 장치에 따라 다릅니다.
#python
brightYCB = cv2.cvtColor(bright, cv2.COLOR_BGR2YCrCb)
darkYCB = cv2.cvtColor(dark, cv2.COLOR_BGR2YCrCb)
//C++
cv::cvtColor(bright, brightYCB, cv::COLOR_BGR2YCrCb);
cv::cvtColor(dark, darkYCB, cv::COLOR_BGR2YCrCb);
YCrCb 색상 공간의 두 이미지를 채널로 분리한 모습이 아래와 같습니다.
YCrCb 구성 요소

그림 4: YCrCb 색 공간의 루마(Y) 및 크로마(Cr, Cb) 구성 요소.
관찰
- LAB와 유사한 관찰은 조도 변화에 따른 강도 및 색상 구성 요소에 대해서도 이루어질 수 있습니다.
- 실외 이미지에서는 LAB에 비해 빨간색과 주황색의 지각적 차이가 덜 균일합니다.
- 흰색은 3가지 구성 요소 모두에서 변화를 겪었습니다.
HSV 색상 공간
HSV 색상 공간에는 다음 세 가지 구성 요소가 있습니다.
- H – 색조(주요 파장).
- S – 채도(순도/색상의 음영).
- V – 값(강도).
그 속성 중 일부를 나열해 보겠습니다.
- 가장 좋은 점은 색상을 설명하는 데 단 하나의 채널(H)만 사용하므로 색상을 지정하는 것이 매우 직관적이라는 것입니다.
- 장치에 따라 다릅니다.
두 이미지의 H, S, V 구성요소는 아래와 같습니다.
#python
brightHSV = cv2.cvtColor(bright, cv2.COLOR_BGR2HSV)
darkHSV = cv2.cvtColor(dark, cv2.COLOR_BGR2HSV)
//C++
cv::cvtColor(bright, brightHSV, cv::COLOR_BGR2HSV);
cv::cvtColor(dark, darkHSV, cv::COLOR_BGR2HSV);
HSV 구성 요소

그림 5: HSV 색상 공간의 색조(H), 채도(S) 및 값(V) 구성 요소.
관찰
- 두 이미지의 H 구성 요소는 매우 유사하며, 이는 조명이 바뀌어도 색상 정보가 그대로 유지됨을 나타냅니다.
- S 구성 요소도 두 이미지에서 매우 유사합니다.
- V 컴포넌트는 입사하는 빛의 양을 포착하므로 조명 변화에 따라 변합니다.
- 실외 이미지와 실내 이미지의 빨간색 부분 값 사이에는 큰 차이가 있습니다. 이는 색상이 원으로 표현되고 빨간색이 시작 각도에 있기 때문입니다. 따라서 [300, 360]에서 [0, 60] 사이의 값을 가질 수 있습니다.
이러한 색상 공간을 분할에 사용하는 방법
가장 간단한 방법
이제 다양한 색상 공간에 대한 아이디어를 얻었으니, 먼저 이를 사용하여 큐브에서 녹색을 감지해 보겠습니다.
1단계: 특정 색상의 색상 값 가져오기
각 색상 공간에서 녹색 값의 대략적인 범위를 구하세요. 이를 위해 아래와 같이 이미지 위에 마우스를 올리기만 하면 각 픽셀의 모든 색상 공간 값을 확인할 수 있는 대화형 GUI를 만들었습니다.
다양한 색상 공간의 픽셀 색상

그림 6: 야외 이미지의 다양한 색상 공간에서 픽셀과 그 값을 보여주는 데모입니다.
2단계: 세분화를 위한 임계값 적용
이미지에서 녹색 픽셀 값과 비슷한 값을 가진 모든 픽셀을 추출합니다. 각 색 공간에 대해 ±40의 범위를 설정하고 결과가 어떻게 나타나는지 확인합니다. OpenCV 함수 inRange를 사용하여 녹색 픽셀의 마스크를 구한 후, 비트 AND 연산을 사용하여 해당 마스크를 사용하여 이미지에서 녹색 픽셀을 추출합니다.
또한, 한 픽셀을 다른 색상 공간으로 변환하려면 먼저 1차원 배열을 3차원 배열로 변환해야 합니다.
#python
bgr = [40, 158, 16]
thresh = 40
minBGR = np.array([bgr[0] - thresh, bgr[1] - thresh, bgr[2] - thresh])
maxBGR = np.array([bgr[0] + thresh, bgr[1] + thresh, bgr[2] + thresh])
maskBGR = cv2.inRange(bright,minBGR,maxBGR)
resultBGR = cv2.bitwise_and(bright, bright, mask = maskBGR)
#convert 1D array to 3D, then convert it to HSV and take the first element
# this will be same as shown in the above figure [65, 229, 158]
hsv = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2HSV)[0][0]
minHSV = np.array([hsv[0] - thresh, hsv[1] - thresh, hsv[2] - thresh])
maxHSV = np.array([hsv[0] + thresh, hsv[1] + thresh, hsv[2] + thresh])
maskHSV = cv2.inRange(brightHSV, minHSV, maxHSV)
resultHSV = cv2.bitwise_and(brightHSV, brightHSV, mask = maskHSV)
#convert 1D array to 3D, then convert it to YCrCb and take the first element
ycb = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2YCrCb)[0][0]
minYCB = np.array([ycb[0] - thresh, ycb[1] - thresh, ycb[2] - thresh])
maxYCB = np.array([ycb[0] + thresh, ycb[1] + thresh, ycb[2] + thresh])
maskYCB = cv2.inRange(brightYCB, minYCB, maxYCB)
resultYCB = cv2.bitwise_and(brightYCB, brightYCB, mask = maskYCB)
#convert 1D array to 3D, then convert it to LAB and take the first element
lab = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2LAB)[0][0]
minLAB = np.array([lab[0] - thresh, lab[1] - thresh, lab[2] - thresh])
maxLAB = np.array([lab[0] + thresh, lab[1] + thresh, lab[2] + thresh])
maskLAB = cv2.inRange(brightLAB, minLAB, maxLAB)
resultLAB = cv2.bitwise_and(brightLAB, brightLAB, mask = maskLAB)
cv2.imshow("Result BGR", resultBGR)
cv2.imshow("Result HSV", resultHSV)
cv2.imshow("Result YCB", resultYCB)
cv2.imshow("Output LAB", resultLAB)
//C++ code
cv::Vec3b bgrPixel(40, 158, 16);
// Create Mat object from vector since cvtColor accepts a Mat object
Mat3b bgr (bgrPixel);
//Convert pixel values to other color spaces.
Mat3b hsv,ycb,lab;
cvtColor(bgr, ycb, COLOR_BGR2YCrCb);
cvtColor(bgr, hsv, COLOR_BGR2HSV);
cvtColor(bgr, lab, COLOR_BGR2Lab);
//Get back the vector from Mat
Vec3b hsvPixel(hsv.at<Vec3b>(0,0));
Vec3b ycbPixel(ycb.at<Vec3b>(0,0));
Vec3b labPixel(lab.at<Vec3b>(0,0));
int thresh = 40;
cv::Scalar minBGR = cv::Scalar(bgrPixel.val[0] - thresh, bgrPixel.val[1] - thresh, bgrPixel.val[2] - thresh)
cv::Scalar maxBGR = cv::Scalar(bgrPixel.val[0] + thresh, bgrPixel.val[1] + thresh, bgrPixel.val[2] + thresh)
cv::Mat maskBGR, resultBGR;
cv::inRange(bright, minBGR, maxBGR, maskBGR);
cv::bitwise_and(bright, bright, resultBGR, maskBGR);
cv::Scalar minHSV = cv::Scalar(hsvPixel.val[0] - thresh, hsvPixel.val[1] - thresh, hsvPixel.val[2] - thresh)
cv::Scalar maxHSV = cv::Scalar(hsvPixel.val[0] + thresh, hsvPixel.val[1] + thresh, hsvPixel.val[2] + thresh)
cv::Mat maskHSV, resultHSV;
cv::inRange(brightHSV, minHSV, maxHSV, maskHSV);
cv::bitwise_and(brightHSV, brightHSV, resultHSV, maskHSV);
cv::Scalar minYCB = cv::Scalar(ycbPixel.val[0] - thresh, ycbPixel.val[1] - thresh, ycbPixel.val[2] - thresh)
cv::Scalar maxYCB = cv::Scalar(ycbPixel.val[0] + thresh, ycbPixel.val[1] + thresh, ycbPixel.val[2] + thresh)
cv::Mat maskYCB, resultYCB;
cv::inRange(brightYCB, minYCB, maxYCB, maskYCB);
cv::bitwise_and(brightYCB, brightYCB, resultYCB, maskYCB);
cv::Scalar minLAB = cv::Scalar(labPixel.val[0] - thresh, labPixel.val[1] - thresh, labPixel.val[2] - thresh)
cv::Scalar maxLAB = cv::Scalar(labPixel.val[0] + thresh, labPixel.val[1] + thresh, labPixel.val[2] + thresh)
cv::Mat maskLAB, resultLAB;
cv::inRange(brightLAB, minLAB, maxLAB, maskLAB);
cv::bitwise_and(brightLAB, brightLAB, resultLAB, maskLAB);
cv2::imshow("Result BGR", resultBGR)
cv2::imshow("Result HSV", resultHSV)
cv2::imshow("Result YCB", resultYCB)
cv2::imshow("Output LAB", resultLAB)
야외 이미지의 녹색 색상에 대한 초기 결과

그림 7 : RGB가 좋아 보입니다. 아마도 여기서 시간을 낭비하고 있는 것일 수도 있습니다.
몇 가지 추가 결과
RGB와 LAB만으로도 색상을 감지하기에 충분하고, 크게 고민할 필요가 없는 것 같습니다. 좀 더 결과를 살펴보겠습니다.
실내 이미지의 녹색 색상에 대한 초기 결과

그림 8: 실내 이미지에 동일한 임계값을 적용해도 모든 색상 공간에서 녹색 큐브를 감지할 수 없습니다.
따라서 어두운 이미지에서는 동일한 임계값이 적용되지 않습니다. 노란색을 감지하기 위해 동일한 실험을 수행하면 다음과 같은 결과가 나옵니다.
야외 이미지의 노란색에 대한 초기 결과

그림 9: 밝은 이미지에서 얻은 동일한 기법과 임계값(노란색)을 사용하여 노란색 조각을 감지해 봅니다. HSV와 YCrCb는 아직 성능이 좋지 않습니다.
실내 이미지의 노란색에 대한 초기 결과

그림 10: 밝은 정육면체에서 얻은 임계값을 사용하여 노란색 조각을 감지하려고 시도합니다. 모든 색공간이 다시 실패합니다.
그런데 왜 결과가 이렇게 나쁠까요? 임계값을 40으로 대충 추측했기 때문입니다. 값을 바꿔가며 모든 이미지에 적합한 값을 찾을 수 있는 인터랙티브 데모도 만들었습니다. 스크린샷을 확인해 보세요. 하지만 다른 이미지가 나왔을 때 다시 작동하지 않는 경우도 있습니다. 맹목적으로 시행착오를 거쳐 임계값을 정할 수는 없습니다. 그렇게 하면 색 공간의 힘을 제대로 활용하지 못하게 됩니다.
올바른 임계값을 찾기 위한 체계적인 방법이 필요합니다.
색상 공간을 가지고 놀기 위한 데모

그림 11: 주어진 이미지의 모든 색상 공간에서 특정 색상을 감지하기 위해 다양한 값을 사용해 실험하는 데모의 스크린샷입니다.
더 나은 솔루션을 위한 몇 가지 데이터 분석
1단계: 데이터 수집
다양한 조명 조건에서 큐브 이미지 10장을 수집하고, 각 색상을 개별적으로 잘라 6가지 색상에 대한 데이터 세트 6개를 얻었습니다. 색상이 얼마나 변하는지 시각적으로 확인할 수 있습니다.

그림 : 조명 조건에 따른 색상 변화 표시
2단계: 밀도 플롯 계산
특정 색상(예: 파란색 또는 노란색)이 다양한 색 공간에서 어떻게 분포하는지 확인하세요. 밀도 플롯 또는 2D 히스토그램은 주어진 색상의 값 변화를 보여줍니다. 예를 들어, 파란색 이미지의 파란색 채널은 이상적으로 항상 255의 값을 가져야 하지만, 실제로는 0에서 255 사이의 값을 가집니다.
BGR 색상 공간에 대한 코드만 보여드립니다. 모든 색상 공간에 대해 이 코드를 작성해야 합니다.
1. 먼저 파란색 또는 노란색 조각의 이미지를 모두 불러옵니다.
#python
B = np.array([])
G = np.array([])
R = np.array([])
im = cv2.imread(fi)
채널을 분리하고 각 이미지의 값을 추가하여 각 채널에 대한 배열을 만듭니다.
#python
b = im[:,:,0]
b = b.reshape(b.shape[0]*b.shape[1])
g = im[:,:,1]
g = g.reshape(g.shape[0]*g.shape[1])
r = im[:,:,2]
r = r.reshape(r.shape[0]*r.shape[1])
B = np.append(B,b)
G = np.append(G,g)
R = np.append(R,r)
matplotlib의 히스토그램 플롯을 사용하여 2D 히스토그램을 그립니다.
#python
nbins = 10
plt.hist2d(B, G, bins=nbins, norm=LogNorm())
plt.xlabel('B')
plt.ylabel('G')
plt.xlim([0,255])
plt.ylim([0,255])
관찰 : 유사한 조명
밀도 플롯 유사 조명

그림 13: 파란색의 두 개의 유사한 밝은 이미지에 대한 색상 채널 값의 변화를 보여주는 밀도 플롯
밀도 플롯 유사 조명

그림 14: 노란색에 대한 2개의 유사한 밝은 이미지에 대한 색상 채널 값의 변화를 보여주는 밀도 플롯
유사한 조명 조건에서 모든 플롯이 매우 조밀하게 배치된 것을 확인할 수 있습니다. 주의할 점은 다음과 같습니다.
- YCrCb와 LAB는 다른 것보다 훨씬 더 컴팩트합니다.
- HSV에서는 S 방향(색 순도)에는 변화가 있지만 H 방향에는 변화가 거의 없습니다.
관찰 : 다른 조명
밀도 플롯 다른 조명

그림 15: 파란색에 대한 다양한 조명 하에서 색상 채널의 값 변화를 보여주는 밀도 플롯
밀도 플롯 다른 조명

그림 16: 노란색에 대한 다양한 조명 하에서 색상 채널의 값 변화를 보여주는 밀도 플롯
조명이 크게 변하면 다음 사항을 확인할 수 있습니다.
- 이상적으로는 색상 채널에 대해 가장 컴팩트하고 집중된 밀도 플롯을 갖춘 색상 공간에서 작업하고 싶습니다.
- RGB 밀도 플롯이 크게 증가합니다. 이는 채널 값의 편차가 매우 크고 임계값을 설정하는 것이 큰 문제임을 의미합니다. 더 높은 범위를 설정하면 원하는 색상과 유사한 색상을 감지하고(False Positives), 더 낮은 범위를 설정하면 다른 조명에서 원하는 색상을 감지하지 못합니다(False Negatives).
- HSV에서는 H 구성 요소만 절대 색상 정보를 포함하기 때문에, YCrCb(Cr과 Cb)와 LAB(A와 B)에서는 두 개의 노브를 사용하여 색상을 지정하는 반면, HSV에서는 노브 하나만(H) 조정하면 되므로 제가 가장 선호하는 색상 공간이 되었습니다.
- YCrCb와 LAB 색공간을 비교해 보면 LAB 색공간의 밀도가 더 높습니다. 따라서 저에게는 LAB 색공간이 차선책입니다.
최종 결과
이 마지막 섹션에서는 두 번째 섹션에서 했던 것과 같은 방식으로 밀도 플롯에서 임계값을 가져와 각 색 공간에 적용하여 파란색과 노란색 부분을 감지한 결과를 보여드리겠습니다. HSV, YCrCb, LAB 색 공간에서 작업할 때는 강도(Intensity) 성분에 대해 신경 쓸 필요가 없습니다. 색상 성분에 대한 임계값만 지정하면 됩니다. 결과를 생성하기 위해 사용한 값은 그림에 표시되어 있습니다.
데모 이미지

그림 17 : 데모 이미지 1
데모 이미지 1에 대한 결과

그림 18: 데모 이미지 1에서 노란색 감지 결과
데모 이미지 1의 결과

그림 19: 데모 이미지 1의 파란색 감지 결과
데모 이미지

그림 20 : 데모 이미지 2
데모 이미지 2의 결과

그림 21: 데모 이미지 2에서 노란색 감지 결과
데모 이미지 2의 결과

그림 22: 데모 이미지 2에서 파란색 감지 결과
데모 이미지

그림 23 : 데모 이미지 3
데모 이미지 3에 대한 결과

그림 24: 데모 이미지 3에서 노란색 감지 결과
데모 이미지 3의 결과

그림 25: 데모 이미지 3에서 파란색 감지 결과
위 결과에서는 밀도 플롯에서 직접 값을 가져왔습니다. 밀도 플롯에서 가장 밀도가 높은 영역에 속하는 값을 선택할 수도 있는데, 이는 색상 범위를 더욱 정밀하게 제어하는 데 도움이 됩니다. 이렇게 하면 일부 구멍과 흩어진 픽셀이 남게 되는데, 이는 침식(Erosion)과 팽창(Dilation) 후 필터링(Filtering)을 통해 제거할 수 있습니다.
색상 공간의 다른 유용한 응용 프로그램
- 히스토그램 평활화는 일반적으로 회색조 이미지에서 수행됩니다. 그러나 RGB 이미지를 YCbCr로 변환하고 Y 채널에 대해서만 히스토그램 평활화를 수행하여 컬러 이미지의 평활화를 수행할 수 있습니다.
- 두 이미지 간의 색상을 Lab 색상 공간으로 변환하여 전송합니다.
- Google 카메라나 Instagram과 같은 스마트폰 카메라 애플리케이션의 많은 필터는 이러한 색 공간 변환을 활용해 멋진 효과를 만듭니다!
PS: 루빅큐브를 푸는 데 관심이 있다면, 이 단계별 가이드를 참고하세요.
'OpenCV' 카테고리의 다른 글
| OpenCV GUI에서 마우스와 트랙바 (0) | 2025.09.11 |
|---|---|
| OpenCV 에지 검출 (0) | 2025.09.11 |
| OpenCV 블롭 탐지 Python, C++ (0) | 2025.09.10 |
| OpenCV 이미지 임계값 처리 Thresholding (0) | 2025.09.10 |
| OpenCV 이미지 변환 및 회전 (0) | 2025.09.09 |
| OpenCV 이미지 자르기 (0) | 2025.09.09 |
| OpenCV 이미지 크기 조정 (0) | 2025.09.09 |
| OpenCV 초보자는 여기서 시작하세요. (0) | 2025.09.08 |
취업, 창업의 막막함, 외주 관리, 제품 부재!
당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약,
아이디어는 있지만 구현할 기술이 없는 막막함.
우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.
이제 고민을 멈추고, 캐어랩을 만나세요!
코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.
제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!
귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.
지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.
귀사가 성공하기까지의 긴 고난의 시간을 캐어랩과 함께 하세요.
캐어랩