본문 바로가기

# 미사용/OpenCV

[OpenCV] 채널의 분리, 병합, 혼합

2020-05-12 수정됨

채널관련 기본 영상처리 함수

OpenCV는 다음과 같은 기본 채널연산을 제공합니다.

  • 채널 분리 (spilt)
  • 채널 병합 (merge)
  • 채널 혼합 (mix)

채널 분리

N채널 영상을 1채널 영상 N개로 분리합니다.


함수 정의

/**
 * src를 1채널 단위로 분리한다.
 * 
 * @param src   분리할 영상.
 * @param dest  분리된 채널들이 저장될 배열.
 */
void spilt(const Mat &src, Mat dest[]);

예제

//! 테스트 3채널 영상.
Mat src = Mat(Size(2, 2), CV_8SC3, Scalar(0, 1, 2));
/*
 * [(0, 1, 2), (0, 1, 2);
 *  (0, 1, 2), (0, 1, 2)]
 */

//! 각 채널을 분리한다.
Mat dest[3];
split(src, dest);
/*
 * dest[0] :
 *   [0, 0;
 *    0, 0]
 */

/*
 * dest[1] :
 *   [1, 1;
 *    1, 1]
 */

/*
 * dest[2] :
 *   [2, 2;
 *    2, 2]
 */

채널 병합

1채널 영상 N개를 N채널 영상 1개로 병합합니다.


함수 정의 1

/**
 * 입력영상들을 단일영상으로 만든다.
 * 
 * @param src   입력영상의 벡터
 * @param dest  출력영상
 */
void merge(vector<Mat> src, Mat dest);

함수 정의 2

/**
 * 입력영상들을 단일영상으로 만든다.
 *
 * @param src   입력영상의 배열
 * @param N     입력영상의 개수
 * @param dest  출력영상
 */
void merge(Mat src[], int N, Mat dest);

벡터를 이용한 구현은 모던하지만,

배열과 길이를 이용한 기법은 C++에서 여전히 유용하게 사용됩니다.


예제

//! 테스트 1채널 영상들.
Mat src[3];
for(int i=0; i<3; i++){
    src[i] = Mat(Size(2, 2), CV_8UC1, Scalar(i));
}
/*
 * src[0] :
 *   [0, 0;
 *    0, 0]
 */

/*
 * src[1] :
 *   [1, 1;
 *    1, 1]
 */

/*
 * src[2] :
 *   [2, 2;
 *    2, 2]
 */


//! 1채널 영상 3개를 병합한다.
Mat dest;
merge(src, 3, dest);
/*
 * dest :
 *   [(0, 1, 2), (0, 1, 2);
 *    (0, 1, 2), (0, 1, 2)]
 */

채널 혼합

단일영상 혼합

먼저 혼합순서가 무엇인지 알아야 합니다.

예제를 보면서 살펴보겠습니다.


예제 1

BGRRGB로 바꾸는 혼합순서는 다음과 같습니다.

  • input[0] -> output[2] (0 -> 2)
  • input[1] -> output[1] (1 -> 1)
  • input[2] -> output[0] (2 -> 0) 이므로,
int bgr_to_rgb_order[] = {
    0, 2,  // 0 -> 2
    1, 1,  // 1 -> 1
    2, 0   // 2 -> 0
};

예제 2

BGRRRR로 바꾸는 혼합순서는 다음과 같습니다.

int fill_to_red_order[] = {
    2, 0,   // 2 -> 0
    2, 1,   // 2 -> 1
    2, 2    // 2 -> 2
};

다중영상 혼합

이번에는 다중영상에서의 혼합순서도 알아보겠습니다.

간단하게 요약하면 모든 영상들의 채널을 일렬로 나열하는 것이 포인트입니다.

이번에도 예시를 보면서 생각해보겠습니다.

//! 입력영상의 배열.
Size size = Size(2, 2);
Mat img1 = Mat(size, CV_8UC3, Scalar(0, 1, 2));
Mat img2 = Mat(size, CV_8UC3, Scalar(3, 4, 5));
Mat input[] = {img1, img2};

//! 출력영상의 배열.
Mat r = Mat(size, CV_8UC2);
Mat g = Mat(size, CV_8UC2);
Mat b = Mat(size, CV_8UC2);
Mat output[] = {r, g, b};

위의 입력배열과 출력배열의 채널들을 나열하면,

다음과 같은 형태가 완성됩니다.

여기서 입력영상은 BGRBGR으로 생각할 수 있고,
출력영상을 BBGGRR로 만들기 위한 혼합순서는 다음과 같이 생각할 수 있습니다.

  • input[0] -> output[0]
  • input[1] -> output[2]
  • input[2] -> output[4]
  • input[3] -> output[1]
  • input[4] -> output[3]
  • input[5] -> output[5]
int mix_order[] = {
    0, 0,
    1, 2,
    2, 4,
    3, 1,
    4, 3,
    5, 5
};

함수 정의 1

/**
 * 입력영상의 채널순서를 변경하여 출력영상으로 내보낸다.
 * 
 * @param src       입력영상의 벡터
 * @param dest      출력영상의 벡터
 * @param order     혼합순서쌍의 벡터
 * @since           OpenCV 4.x
 */
void mixChannels(vector<mat> src, vector<mat> dest, vector<int> order);

함수 정의 2

/**
 * 입력영상 N개의 채널순서를 변경하여,
 * 출력영상 M개로 내보낸다.
 * 
 * @param src       입력영상의 배열
 * @param src_n     입력의 수
 * @param dest      출력영상의 배열
 * @param dest_n    출력의 수
 * @param pairs     혼합순서쌍의 배열
 * @param pairs_n   혼합순서쌍의 개수
 */
void mixChannels(
        Mat src[], size_t src_n, 
        Mat dest[], size_t dest_n, 
        int pairs[], size_t pairs_n);

STL 벡터는 깔끔하지만, 오래된 OpenCV에는 이 구현이 없으므로,

C++ 관용구인 포인터와 길이를 같이 넘겨주는 기법도 기억해두면 좋습니다.


예제

2개의 BGRA 영상을, 아래처럼 4개의 영상으로 나눠보겠습니다.

  • 첫 번째 영상의 RGB 영상.
  • 첫 번째 영상의 Alpha 영상.
  • 두 번째 영상의 RGB 영상.
  • 두 번째 영상의 Alpha 영상.

//! 입력영상의 배열.
Size size = Size(2, 2);
Mat a = Mat(size, CV_8UC4, Scalar(0, 1, 2, 3));
Mat b = Mat(size, CV_8UC4, Scalar(3, 4, 5, 6));
Mat input[] = {a, b};

//! 출력영상의 배열.
Mat a_rgb = Mat(size, CV_8UC3);
Mat a_a   = Mat(size, CV_8UC1);
Mat b_rgb = Mat(size, CV_8UC3);
Mat b_a   = Mat(size, CV_8UC1);
Mat output[] = {a_rgb, a_a, b_rgb, b_a};

//! 혼합순서
int order[] = {
        0, 2,
        1, 1,
        2, 0,
        3, 3,
        4, 6,
        5, 5,
        6, 4,
        7, 7
};

mixChannels(input, 2, output, 4, order, 8);

'# 미사용 > OpenCV' 카테고리의 다른 글

[OpenCV] 행렬의 비트연산  (0) 2019.11.01
[OpenCV] 행렬의 산술연산  (0) 2019.10.31
[OpenCV] 행렬의 대칭, 전치, 반복  (0) 2019.10.30
[OpenCV] 기본도형 그리기  (0) 2019.10.29
[OpenCV] 윈도우 및 이벤트 관리  (0) 2019.10.29