비디오 코덱

이미지 밝기 조절 : Histogram, calcHist 이용 (1)

케케_ 2024. 11. 28. 14:11

히스토그램 개념

(1) 히스토그램?

  • 이미지의 픽셀 값이 어떻게 분포돼 있는지 나타내는 그래프
  • 가로축 : 픽셀값 (밝기 수준, 0 ~ 255)
  • 세로축 : 해당 픽셀값을 가진 픽셀의 개수

(2) 히스토그램의 특징

  • 밝기 분포를 분석할 수 있음
  • 이미지가 어두운지, 밝은지, 혹은 대비(contrast)가 낮은지 쉽게 파악 가능
  • 예:
    • 어두운 이미지: 픽셀 값이 0에 몰려 있음
    • 밝은 이미지: 픽셀 값이 255에 몰려 있음
    • 대비가 낮은 이미지: 픽셀 값이 특정 영역에만 분포

 


OpenCV에서 히스토그램 계산

: openCV에서 cv::calcHist를 사용해 히스토그램 계산

 

함수 정의

cv::calcHist(const Mat* images, int nimages, const int* channels,
             InputArray mask, OutputArray hist, int dims, const int* histSize,
             const float** ranges, bool uniform=true, bool accumulate=false);

 

주요 매개변수

  1. images: 입력 이미지 (1채널 또는 3채널 cv::Mat)에 대한 배열
  2. nimages: 이미지 수 (보통 1).
  3. channels: 계산할 채널 (예: Grayscale은 [0], BGR은 [0, 1, 2]).
  4. mask: 특정 영역의 히스토그램만 계산할 때 사용 (cv::Mat::ones로 전체 영역 사용) --> (옵션) 사용 안할 경우 cv::Mat()로
  5. hist: 출력 히스토그램 (결과 저장 변수).
  6. dims: 히스토그램 차원 (보통 1).
  7. histSize: 히스토그램의 bin 개수 (예: [256]) - 빈도수를 분류할 칸의 개수
  8. ranges: 픽셀 값 범위 (예: [0, 256]).

 

추가 개념

(1) bin

  • 히스토그램의 bin
    • 픽셀 값을 그룹으로 나누는 단위
    • 픽셀 값 0~255를 bin 개수에 따라 분리:
      • 256개 bin → 각 bin이 하나의 픽셀 값(예: 0, 1, ..., 255) 담당.
      • 128개 bin → 각 bin이 두 개의 픽셀 값 담당(예: [0, 1], [2, 3], ...)
  • bin 개수의 선택
    • bin 개수가 크면 더 정밀한 히스토그램.
    • bin 개수가 작으면 더 간단한 히스토그램.
  • 왜 예제에서 256 사용?
    • Grayscale 이미지는 0~255의 256가지 픽셀 값을 가지므로, 각 값에 대응하는 bin을 생성하기 위해 bin 개수를 256으로 설정.

 

(2) cv::Mat::ones와 cv::Mat()

  • cv::Mat::ones
    • 초기화된 Mat 생성자
      • 지정된 크기와 데이터 유형을 가진 1로 초기화된 행렬을 생성 
    • 메모리가 할당되고 모든 요소가 1로 초기화
cv::Mat mat = cv::Mat::ones(3, 3, CV_8U); // 3x3 크기의 8비트 단일 채널 이미지, 모든 값이 1

 

  • cv::Mat()
    • 기본 생성자
      • 크기와 값을 지정하지 않은 빈 cv::Mat 객체를 생성.
      • 메모리는 할당되지 않음.

 

 

(3) 배열 사용 이유 (int histSize[], const float* histRange[])

  • openCV 유연성을 위한 설계
    • cv::calcHist 함수는 다양한 차원의 히스토그램을 계산할 수 있도록 설계됨
      • histSize[]:
        • 각 차원의 bin 개수를 지정.
        • 예: 1D 히스토그램 → {256}, 2D 히스토그램 → {256, 256}.
      • histRange[]:
        • 각 차원의 값 범위를 지정.
        • 예: 1D 히스토그램 → {[0, 256]}, 2D 히스토그램 → {[0, 256], [0, 256]}.

 

(4) 픽셀 값 범위가 0~255인데 왜 256을 쓰는가?

  • 히스토그램 범위
    • 히스토그램 계산에서 범위 끝값은 포함되지 않습니다.
      • 즉, [0, 256) 범위는 0~255 픽셀 값을 포함.
      • OpenCV는 cv::calcHist에서 마지막 값을 포함하지 않는 방식으로 작동.
float range[] = {0, 256}; // 실제로는 0~255 값만 계산됨

 

  • bin의 수와 범위의 관계
    • bin 개수가 256이고, 범위가 [0, 256)이면 각 bin이 정확히 1개의 픽셀 값을 담당.
      • bin 0: 픽셀 값 0.
      • bin 1: 픽셀 값 1.
      • ...
      • bin 255: 픽셀 값 255.

 


단일 채널 히스토그램 계산

 

코트

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

int main() {
    cv::Mat img_1 = cv::imread("../test.png",0);
    
    if(img_1.empty()) {
        std::cerr << "Error: Cannot load image!" << std::endl;
        return -1;
    }

    //히스토그램 계산
    cv::Mat hist;
    int histSize[] = {256}; //bin의 수 , histSize[0]=256
    float range[] = {0, 256};    //픽셀값 범위
    const float* histRange[] = {range};
    bool uniform = true, accumulate = false;
    cv::calcHist(&img_1, 1, 0, cv::Mat(), hist, 1, histSize, histRange, uniform, accumulate);

    std::cout<< "Hoistogram : " << hist << std::endl;

    cv::waitKey(0);
    return 0;
}

 

 

결과

 

다음 글에서 위 데이터를 그래프로 출력해 보도록하겠다.