← .mpy 공유

DN 히스토그램 누적 분석

영상 센서의 DN(Digital Number)별 픽셀 수를 로그 스케일과 역누적 곡선으로 분석한다

English

Analyzes pixel counts per DN on log scale with a reverse-cumulative curve

실행 결과 미리보기가 없는 스크립트입니다

README

DN 히스토그램 누적 분석

설명

영상 센서의 DN(Digital Number)별 픽셀 수를 로그 스케일과 역누적 곡선으로 분석한다

필요 데이터

DN 값 컬럼 1개 + 개수(Count) 컬럼 1개 (실행 시 선택)

출력

DN-Count 로그 산점도 + 역누적 곡선 그래프, 역누적 표

필요 패키지

  • matplotlib
  • pandas

태그

science, visualization, statistics

작성자

SDSL | MDS 1.0

Source · 65줄
# MDS_TITLE: DN 히스토그램 누적 분석
# MDS_REQUIRES: matplotlib, pandas
# MDS_DESC: 영상 센서의 DN(Digital Number)별 픽셀 수를 로그 스케일과 역누적 곡선으로 분석한다
# MDS_DESC[en]: Analyzes pixel counts per DN on log scale with a reverse-cumulative curve
# MDS_TAGS: science, visualization, statistics
# MDS_INPUT: DN 값 컬럼 1개 + 개수(Count) 컬럼 1개 (실행 시 선택)
# MDS_OUTPUT: DN-Count 로그 산점도 + 역누적 곡선 그래프, 역누적 표
# MDS_AUTHOR: SDSL
# MDS_VERSION: 1.0
def apply_plot_style() -> None:
    try:
        plt.style.use('seaborn-v0_8-whitegrid')
    except OSError:
        plt.style.use('seaborn-whitegrid')
    plt.rcParams['font.family'] = 'Malgun Gothic'
    plt.rcParams['axes.unicode_minus'] = False
    return None


def reverse_cumulative(df: pd.DataFrame, dn_col: str, count_col: str) -> pd.DataFrame:
    dff = df[[dn_col, count_col]].dropna().copy()
    dff = dff.groupby(dn_col, as_index=False)[count_col].sum().sort_values(dn_col)
    dff['역누적_Count'] = dff[count_col][::-1].cumsum()[::-1]
    return dff.reset_index(drop=True)


def plot_histogram(table: pd.DataFrame, dn_col: str, count_col: str) -> None:
    apply_plot_style()
    fig, axes = plt.subplots(1, 2, figsize=(13, 5))
    axes[0].scatter(table[dn_col], table[count_col], s=22, alpha=0.7, color='#4C72B0',
                    edgecolors='white', linewidths=0.4)
    axes[0].set_yscale('log')
    axes[0].set_title('DN vs Count (Log Scale)', fontsize=12, fontweight='semibold')
    axes[0].set_xlabel(dn_col)
    axes[0].set_ylabel('Count (log)')
    axes[1].plot(table[dn_col], table['역누적_Count'], linewidth=1.6, color='#DD8452')
    axes[1].fill_between(table[dn_col], table['역누적_Count'], alpha=0.12, color='#DD8452')
    axes[1].set_yscale('log')
    axes[1].set_title('DN 이상 픽셀 수 (역누적)', fontsize=12, fontweight='semibold')
    axes[1].set_xlabel(dn_col)
    axes[1].set_ylabel('역누적 Count (log)')
    for ax in axes:
        ax.grid(True, alpha=0.3, which='both')
    plt.tight_layout()
    plt.show()
    return None


def run_dn_histogram(df: pd.DataFrame) -> pd.DataFrame | None:
    numeric_cols = df.select_dtypes(include='number').columns.tolist()
    if len(numeric_cols) < 2:
        mIO.print('수치형 컬럼이 2개 이상 필요합니다 (DN, Count).')
        return None
    dn_col = mIO.combo('DN 컬럼을 선택하세요', numeric_cols, default=numeric_cols[0])
    count_col = mIO.combo('Count 컬럼을 선택하세요', numeric_cols, default=numeric_cols[1])
    if dn_col is None or count_col is None or dn_col == count_col:
        mIO.print('서로 다른 DN / Count 컬럼을 선택해야 합니다.')
        return None
    table = reverse_cumulative(df, dn_col, count_col)
    plot_histogram(table, dn_col, count_col)
    return table


result = run_dn_histogram(df)