티스토리 뷰

프로그래밍/MFC 프로그래밍

MFC 히스토그램 그리기

주식하는 개발자 2020. 1. 7. 21:17

[MFC 히스토그램 그리기]

오늘은 히스토그램을 출력시키는 예제를 만들어보겠습니다.

 

 

 

[결과 사진]

 

우선 히스토그램의 출력 결과 사진입니다.

RGB 중 R값만 히스토그램 출력한 그림입니다.

 

 


[코드 설명]

 

히스토그램을 그리기 전에

간단하게 외부 이미지 파일 출력하는 방법에 대해 설명드리겠습니다.

먼저 헤더파일의 Dlg.h 파일에 가셔서

CImage변수로 m_image를 추가해줍니다.

외부 이미지를 저장시켜줄 변수입니다.

// CHistogramExamDlg 대화 상자
class CHistogramExamDlg : public CDialogEx
{
private:
    CImage m_image;
// 생성입니다.
public:
    CHistogramExamDlg(CWnd* pParent = nullptr);    // 표준 생성자입니다.
cs

 

 

다음으로

소스 파일의 Dlg.cpp파일에 가서

OnInitDialog() 함수가 있는 위치로 이동해줍니다.

m_image를 로드시켜주기 위해

아래 코드를 추가시켜줍니다.

m_image.Load(L"test.bmp");

 

사용자가 출력시키고 싶은 사진은

해당 프로젝트 소스파일들이 모여있는 폴더에 저장시켜줘야합니다.

저는 파일명은 test 로하고

.bmp로 저장시켜줬습니다.

BOOL CHistogramExamDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
 
    SetIcon(m_hIcon, TRUE);            // 큰 아이콘을 설정합니다.
    SetIcon(m_hIcon, FALSE);        // 작은 아이콘을 설정합니다.
 
    m_image.Load(L"test.bmp");
 
    return TRUE;  // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}
cs

 

 

다음은 버튼를 누르면 히스토그램이 출력되게 만들것이기 때문에

버튼을 만들어주어야합니다.

 

리소스 뷰에 있는 Dialog로 이동해줍니다.

다이얼로그 편집하는 곳으로 이동했으면

도구상자에서 버튼 하나를 가져와 추가한 후에

버튼을 더블클릭해 줍니다.

그러면 소스파일 안에 Dlg.cpp 파일에 버튼 이벤트 처리를 해줄 함수가 하나 생기는데

아래 코드를 추가시켜줍니다.

m_image.Draw(dc, 0, 0);

버튼이 눌렸을때 이미지가 (0,0) 좌표에 그려지는 코드입니다.

void CHistogramExamDlg::OnBnClickedButton1()
{
    CClientDC dc(this);
    m_image.Draw(dc, 00);
}
cs

 

 

이제 코드 추가는 끝이났고

프로그램을 실행시키면 아래와 같은 결과가 나오게됩니다.

 

그리고 버튼을 누르면 이미지가 출력됩니다.

여기까지가 외부 이미지 출력을 출력하는 방법이었습니다.

 

 

 

 



히스토그램을 만드는 예제를 시작하겠습니다.

소스파일의 Dlg.h 파일에 아래와 같이 배열을 각각 만들어줍니다.

픽셀의 R,G,B 색상을 각각 저장시킬 배열입니다.

// CHistogramExamDlg 대화 상자
class CHistogramExamDlg : public CDialogEx
{
private:
    CImage m_image;
    int his_arr_r[256]; // rgb 중 r값을 저장할 배열
    int his_arr_g[256]; // g값을 저장할 배열
    int his_arr_b[256]; // b값을 저장할 배열
// 생성입니다.
public:
    CHistogramExamDlg(CWnd* pParent = nullptr);    // 표준 생성자입니다.
 
cs

 

 

그리고 리소스 뷰에 있는 Dialog에 가셔서

아래와 같이 이미지 출력하는 버튼,

히스토그램 그래프 출력하는 버튼,

히스토그램 계산하는 버튼 3가지를 만들어줍니다.

 

 

 

먼저 히스토그램 계산하는 버튼을 더블클릭해주시고

함수가 만들어지면 아래의 코드를 추가시켜줍니다.

 

GetHeight()와 GetWidht()는 이미지의 높이와 가로 픽셀수를 얻어오는 함수입니다.

이것을 사용해 이미지의 전체 픽셀을 for문을 사용해 돌아줍니다.

GetPixel은 해당 좌표에 픽셀값을 가져오는 함수입니다.

void CHistogramExamDlg::OnBnClickedButton3()
{
    CClientDC dc(this);
 
    COLORREF temp_color;
 
    int color_count = 0, x, y, i;
 
    for (y = 0; y < m_image.GetHeight(); y++) {    // 이미지의 세로픽셀
        for (x = 0; x < m_image.GetWidth(); x++) { // 이미지의 가로픽셀
 
            temp_color = dc.GetPixel(x, y); // x,y 위치의 색상을 가져와서 temp_color에 저장
 
            BYTE r = GetRValue(temp_color); // temp_color에 저장되있는 색상중 r값만 가져와 저장
            BYTE g = GetGValue(temp_color); // temp_color에 저장되있는 색상중 g값만 가져와 저장
            BYTE b = GetBValue(temp_color); // temp_color에 저장되있는 색상중 b값만 가져와 저장
 
            his_arr_r[(int)r]++; // 해당r값의 배열에 +1을 해줌
            his_arr_g[(int)g]++; // 해당g값의 배열에 +1을 해줌
            his_arr_b[(int)b]++; // 해당b값의 배열에 +1을 해줌
        }
    }
}
cs

 

 

 

 

그리고 다시 리소스 뷰에 있는 Dialog로 가서

히스토그램 버튼을 더블클릭해주시고

아래의 코드를 추가시켜줍니다.

void CHistogramExamDlg::OnBnClickedButton2()
{
    CClientDC dc(this);
 
    for (int i = 0; i < 256; i++) {
        dc.Rectangle(500 + (i * 2), 800 - his_arr_r[i], 502 + (i * 2), 800);
    }
}
cs

(위의 코드는 히스토그램을 가로가 2픽셀인 사각형으로 그려주는 코드입니다.)

 

 

 

이러면 코드 추가는 끝이 났고

프로그램을 실행시켜줍니다.

 

 

 

아래는 프로그램 결과 사진입니다.

버튼 순서는

이미지 로드 -> 히스토그램 계산 -> 히스토그램 순서대로 눌러줍니다.

히스토그램 계산 버튼에서 시간이 몇 분 소요되실 수도 있습니다...

픽셀 값만큼 for문을 돌기 때문입니다.

 

 

아래는 Dlg.cpp 파일 전체 소스입니다.

// HistogramExamDlg.cpp: 구현 파일
//
 
#include "pch.h"
#include "framework.h"
#include "HistogramExam.h"
#include "HistogramExamDlg.h"
#include "afxdialogex.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
// CHistogramExamDlg 대화 상자
 
CHistogramExamDlg::CHistogramExamDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_HISTOGRAMEXAM_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
 
void CHistogramExamDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}
 
BEGIN_MESSAGE_MAP(CHistogramExamDlg, CDialogEx)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON1, &CHistogramExamDlg::OnBnClickedButton1)
    ON_BN_CLICKED(IDC_BUTTON2, &CHistogramExamDlg::OnBnClickedButton2)
    ON_BN_CLICKED(IDC_BUTTON3, &CHistogramExamDlg::OnBnClickedButton3)
END_MESSAGE_MAP()
 
 
// CHistogramExamDlg 메시지 처리기
 
BOOL CHistogramExamDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
 
    SetIcon(m_hIcon, TRUE);            // 큰 아이콘을 설정합니다.
    SetIcon(m_hIcon, FALSE);        // 작은 아이콘을 설정합니다.
 
    m_image.Load(L"test.bmp");
 
 
 
    return TRUE;  // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}
 
 
void CHistogramExamDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.
 
        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
 
        // 클라이언트 사각형에서 아이콘을 가운데에 맞춥니다.
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1/ 2;
        int y = (rect.Height() - cyIcon + 1/ 2;
 
        // 아이콘을 그립니다.
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }
}
// 사용자가 최소화된 창을 끄는 동안에 커서가 표시되도록 시스템에서
//  이 함수를 호출합니다.
HCURSOR CHistogramExamDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}
 
void CHistogramExamDlg::OnBnClickedButton1()
{
    CClientDC dc(this);
    m_image.Draw(dc, 00);
}
 
void CHistogramExamDlg::OnBnClickedButton2()
{
    CClientDC dc(this);
 
    for (int i = 0; i < 255; i++) {
        dc.Rectangle(500 + (i * 2), 800 - his_arr_r[i], 502 + (i * 2), 800);
    }
}
 
void CHistogramExamDlg::OnBnClickedButton3()
{
    CClientDC dc(this);
 
    COLORREF temp_color;
 
    int color_count = 0, x, y, i;
 
    for (y = 0; y < m_image.GetHeight(); y++) {
        for (x = 0; x < m_image.GetWidth(); x++) {
 
            temp_color = dc.GetPixel(x, y); // x,y 위치의 색상을 가져와서 temp_color에 저장
 
            BYTE r = GetRValue(temp_color);
            BYTE g = GetGValue(temp_color);
            BYTE b = GetBValue(temp_color);
 
            his_arr_r[(int)r]++;
            his_arr_g[(int)g]++;
            his_arr_b[(int)b]++;
        }
    }
}
cs

이상입니다~:)