상세 컨텐츠

본문 제목

Histogram

Coding/Image

by linguana 2021. 5. 7. 12:10

본문

OpenCV Image Histograms ( cv2.calcHist ) - PyImageSearch

 

OpenCV Image Histograms ( cv2.calcHist ) - PyImageSearch

In this tutorial, you will learn how to compute image histograms using OpenCV and the “cv2.calcHist” function.

www.pyimagesearch.com

2021년 4월 28일 포스팅

[24편] 이미지 히스토그램 균일화 : 네이버 블로그 (naver.com)


들어가며

cv2.calcHist를 활용하여 이미지 히스토그램을 계산하는 방식에 대해서 배워봅시다.

히스토그램은 컴퓨터 비전의 거의 모든 분야에서 널리 사용됩니다:

  • Thresholding (그레이 스케일 히스토그램 활용)
  • White balancing
  • CamShift 알고리즘과 같은 객체 추적 (컬러 스케일 히스토그램 사용)
  • HOG 및 SIFT descriptor 추출
  • bag-of-visual-words representation (이미지 검색 엔진과 머신 러닝에 사용)

그렇다면 히스토그램이 그렇게 유용한 이유는 뭘까요?

히스토그램은 어떤 데이터의 빈도 분포(frequency distribution)를 보여주기 때문입니다. 이러한 빈도 분포를 검토하는 것은 머신러닝 알고리즘과 함께 사용하면 간단한 이미지 처리 기술을 구축하기에 아주 좋습니다.

이 글에서는 그레이 스케일과 컬러 스케일 히스토그램을 계산하는 방법을 포함하여 이미지 히스토그램에 대해 소개를 하겠습니다.


목차

  • 이미지 히스토그램이란?
  • 그레이 스케일 히스토그램 소스코드
  • 컬러 스케일 히스토그램 소스코드
  • 일부 구역에 대한 히스토그램 추출하는 소스코드

이미지 히스토그램이란?

히스토그램은 (컬러 혹은 흑백) 이미지 내 픽셀 강도(pixel intensities) 분포를 나타냅니다. 


그레이 스케일 히스토그램 소스코드

# import the necessary packages
from matplotlib import pyplot as plt
import argparse
import cv2

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to the image")
args = vars(ap.parse_args())

# load the input image and convert it to grayscale
image = cv2.imread(args["image"])
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# compute a grayscale histogram
hist = cv2.calcHist([image], [0], None, [256], [0, 256])

# matplotlib expects RGB images so convert and then display the image
# with matplotlib
plt.figure()
plt.axis("off")
plt.imshow(cv2.cvtColor(image, cv2.COLOR_GRAY2RGB))

# plot the histogram
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
plt.plot(hist)
plt.xlim([0, 256])

코드 실행하기↓

$ python grayscale_histogram.py --image beach.png

결과:

<그림> 원본 이미지와 그레이 스케일 히스토그램
<그림> 좌: 일반 히스토그램; 우: 정규화된 히스토그램. y축 유의할 것


import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
import math


# Line Histogram
def Line_Histogram(gray_img):
    line_sum = gray_img.sum(axis=1)
    # print(line_sum)
    hist_label = np.arange(0, gray_img.shape[0])
    # print(line_sum.shape)
    # print(hist_label.shape)

    plt.figure(figsize=(10, 10))
    plt.barh(hist_label, line_sum)
    plt.show()

# Word Histogram
def Word_Histogram(Line_img):
    word_sum = Line_img.sum(axis=0)
    word_label = np.arange(0, Line_img.shape[1])

    plt.figure(figsize=(10, 5))
    plt.bar(word_label, word_sum)
    plt.show()

# Line Split
def Line_Split(gray_img):
    line_sum = gray_img.sum(axis=1)
    # print(line_sum)
    line_idx = []
    sign = False        # True: 추출중, False: 0인 지점
    for i in range(line_sum.shape[0]):
        if sign == False:
            if line_sum[i] == 0:
                continue
            line_idx.append(i)
            sign = True
        else:   # sign == True
            if i == line_sum.shape[0]-1:    # 마지막 pixel이 0이 아닐때 생기는 오류 방지
                line_idx.append(i -1)
                break
            if line_sum[i] != 0:
                continue
            line_idx.append(i)
            sign = False

    # print(line_idx)
    line_img = []
    for k in range(0, len(line_idx), 2):
        line_img.append(img2[line_idx[k]: line_idx[k + 1], :])

    return line_img, line_idx

# Word Split
def Word_Split(line_img, line_idx, num, avg_distance): # line_img: numpy
    word_sum = line_img.sum(axis=0)
    word_idx = []    
    sign = False                                                                     # True: 추출중, False: 0인 지점
    for i in range(word_sum.shape[0]):
        if sign == False:
            if word_sum[i] == 0:
                continue
            word_idx.append(i)
            sign = True
        else:   # sign == True
            if word_sum[i] != 0:
                continue
            word_idx.append(i)
            sign = False

    # 1. 필요없는 작은 문자(1 Pixel) 지워주기: 왜 생기는지는 모르겠다. 위 erode로 인해 생기는것 같다.
    del_list = []
    for i in range(0, len(word_idx), 2):
        diff = word_idx[i+1] - word_idx[i]
        if diff == 1:
            del_list.append(word_idx[i])
            del_list.append(word_idx[i + 1])
    # print(del_list)
    for j in range(0, len(del_list), 2):
        word_idx.remove(del_list[j])
        word_idx.remove(del_list[j + 1])
    # print(word_idx)



    # 2. '가' -> 'ㄱ' 'ㅏ'로 나오는 현상 없애주기: 각 글자의 중심값을 계산하여 이어주기
    # 글자 하나하나의 중심값 찾아주기
    # line_idx: 하나의 line
    # word_idx: 한 line내 글자들의 위치
    # img2[line_idx[0]: line_idx[1], word_idx[i]: word_idx[i + 1]]

    # 글자 이미지 잘라주기 -> 위의 잘라준 글자 이미지는 높이가 똑같다. 그래서 글자 자체의 높이를 구한다.
    wline_idx = []
    for i in range(0, len(word_idx), 2):
        wline_sum = img2[line_idx[0]: line_idx[1], word_idx[i]: word_idx[i + 1]].sum(axis = 1)
        box = []
        sign = False        # True: 추출중, False: 0인 지점
        for i in range(wline_sum.shape[0]):
            if sign == False:
                if wline_sum[i] == 0:
                    continue
                box.append(i)
                sign = True
            else:   # sign == True
                if i == wline_sum.shape[0]-1:    # 마지막 pixel이 0이 아닐때 생기는 오류 방지
                    box.append(i -1)
                    break
                if wline_sum[i] != 0:
                    continue
                box.append(i)
                sign = False
        wline_idx.append(box[0])
        wline_idx.append(box[-1])
    # print(wline_idx)

    # 이미지의 중심값
    text_center_loc = []
    for i in range(0, len(word_idx), 2):
        x_center_loc = word_idx[i] + (word_idx[i + 1] - word_idx[i]) / 2
        y_center_loc = wline_idx[i] + (wline_idx[i] - wline_idx[i + 1]) / 2
        text_center_loc.append(x_center_loc)
        text_center_loc.append(y_center_loc)
    
    # Text끼리의 중심값 거리
    distance_list = []
    dis_avg = 0
    for i in range(0, len(word_idx) - 2, 2):
        distance = math.sqrt((text_center_loc[i + 2] - text_center_loc[i])**2 + (text_center_loc[i + 3] - text_center_loc[i + 1])**2)
        distance_list.append(distance)
    print(distance_list)
    # print(np.array(text_center_loc).shape)
    # print(np.array(distance_list).shape)

    # # 2. '가' -> 'ㄱ' 'ㅏ'로 나오는 현상 없애주기: 각 글자의 중심값을 계산하여 이어주기
    del_list = []
    sign = True
    for i in range(0, len(word_idx) - 2, 2):
        distance = math.sqrt((text_center_loc[i + 2] - text_center_loc[i])**2 + (text_center_loc[i + 3] - text_center_loc[i + 1])**2)
        if distance <= (avg_distance*0.2):
            continue
        if distance <= (avg_distance*0.75) and sign == True:
            del_list.append(word_idx[i + 1])
            del_list.append(word_idx[i + 2])
            sign = False
        else:
            sign = True

    # and c_subtract > 25

    print(np.array(word_idx).shape) 
    for j in range(0, len(del_list), 2):
        word_idx.remove(del_list[j])
        word_idx.remove(del_list[j + 1])



    # 확인
    # for i in range(0, len(word_idx), 2):
    #     cv.imshow('test', line_img[wline_idx[i]: wline_idx[i + 1], word_idx[i]: word_idx[i + 1]])
    #     cv.waitKey(0)
    #     cv.destroyAllWindows()

    word_img = []
    for k in range(0, len(word_idx), 2):
        word_img.append(img[line_idx[0]: line_idx[1], word_idx[k]: word_idx[k + 1]])
        cv.imwrite('F:/Team Project/Image_data/test_picture/test' + str(num) + '_' + str(k) + '.png', img[line_idx[0]: line_idx[1], word_idx[k]: word_idx[k + 1]])
        word = img[line_idx[0] : line_idx[1] , word_idx[k]: word_idx[k + 1]]
        # word = cv.resize(word, dsize = (64, 64))
        cv.imshow('image', word)
        cv.waitKey(0)
        cv.destroyAllWindows()
    return word_img

# Center distance
def Center_Distance(line_img, line_idx): # line_img: numpy
    word_sum = line_img.sum(axis=0)
    word_idx = []

    sign = False        # True: 추출중, False: 0인 지점
    for i in range(word_sum.shape[0]):
        if sign == False:
            if word_sum[i] == 0:
                continue
            word_idx.append(i)
            sign = True
        else:   # sign == True
            if word_sum[i] != 0:
                continue
            word_idx.append(i)
            sign = False

    # 1. 필요없는 작은 문자(1 Pixel) 지워주기: 왜 생기는지는 모르겠다. 위 erode로 인해 생기는것 같다.
    del_list = []
    for i in range(0, len(word_idx), 2):
        diff = word_idx[i+1] - word_idx[i]
        if diff == 1:
            del_list.append(word_idx[i])
            del_list.append(word_idx[i + 1])
    # print(del_list)
    for j in range(0, len(del_list), 2):
        word_idx.remove(del_list[j])
        word_idx.remove(del_list[j + 1])
    # print(word_idx)


    # 2. '가' -> 'ㄱ' 'ㅏ'로 나오는 현상 없애주기: 각 글자의 중심값을 계산하여 이어주기
    # 글자 하나하나의 중심값 찾아주기
    # line_idx: 하나의 line
    # word_idx: 한 line내 글자들의 위치
    # img2[line_idx[0]: line_idx[1], word_idx[i]: word_idx[i + 1]]

    # 글자 이미지 잘라주기 -> 위의 잘라준 글자 이미지는 높이가 똑같다. 그래서 글자 자체의 높이를 구한다.
    wline_idx = []
    for i in range(0, len(word_idx), 2):
        wline_sum = img2[line_idx[0]: line_idx[1], word_idx[i]: word_idx[i + 1]].sum(axis = 1)
        box = []
        sign = False        # True: 추출중, False: 0인 지점
        for i in range(wline_sum.shape[0]):
            if sign == False:
                if wline_sum[i] == 0:
                    continue
                box.append(i)
                sign = True
            else:   # sign == True
                if i == wline_sum.shape[0]-1:    # 마지막 pixel이 0이 아닐때 생기는 오류 방지
                    box.append(i -1)
                    break
                if wline_sum[i] != 0:
                    continue
                box.append(i)
                sign = False
        wline_idx.append(box[0])
        wline_idx.append(box[-1])
    # print(wline_idx)

    # 이미지의 중심값
    text_center_loc = []
    for i in range(0, len(word_idx), 2):
        x_center_loc = word_idx[i] + (word_idx[i + 1] - word_idx[i]) / 2
        y_center_loc = wline_idx[i] + (wline_idx[i] - wline_idx[i + 1]) / 2
        text_center_loc.append(x_center_loc)
        text_center_loc.append(y_center_loc)
    
    # Text끼리의 중심값 거리
    distance_list = []
    dis_avg = 0
    for i in range(0, len(word_idx) - 2, 2):
        distance = math.sqrt((text_center_loc[i + 2] - text_center_loc[i])**2 + (text_center_loc[i + 3] - text_center_loc[i + 1])**2)
        distance_list.append(distance)
        dis_avg = sum(distance_list)/len(distance_list)
    return dis_avg

# 적용
# 'F:/Team Project/OCR/Text_detection/Image_data/01.jpg'
# 'F:/Team Project/OCR/Text_detection/Image_data/0011.jpg'
# 'F:/Team Project/OCR/Text_detection/Image_data/0012.jpg'
# 'F:/Team Project/OCR/Text_detection/Image_data/0013.jpg'

# 'F:/Team Project/OCR/Text_detection/Image_data/02.jpg'
# 'F:/Team Project/OCR/Text_detection/Image_data/03.png'
# 'F:/Team Project/OCR/Text_detection/Image_data/ex04.png'
# F:/Team Project/OCR/01_Text_detection/Image_data/test_data_OCR/taeyeon
img = cv.imread('F:/Team Project/OCR/01_Text_detection/Image_data/test_data_OCR/taeyeon/Image00010.jpg', cv.IMREAD_COLOR)
img2 = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
img2 = cv.bitwise_not(img2)
img2 = np.where(img2 < 120, 0, 255)
img2 = img2/255.

kernel = np.ones((2, 2), np.uint8)
img2 = cv.morphologyEx(img2, cv.MORPH_CLOSE, kernel)

# kernel = np.ones((2, 2), np.int)
# img2 = cv.erode(img2, kernel, iterations=1)

# kernel = np.array([[0, -1, 0],
#                    [-1, 20,-1],
#                    [0, -1, 0]]) # 커널을 만듭니다.

# # 이미지를 선명하게 만듭니다.
# img2 = cv.filter2D(img2, -1, kernel)


# print(img2.shape)   # (2400, 1080)
# print(img2)
cv.imshow('img', img2)
cv.waitKey(0)
cv.destroyAllWindows()


Line_Histogram(img2)
line_img, line_idx = Line_Split(img2)

# Center_Distance
dis_avg = []
for i in range(len(line_img)):
    dis_avg.append(Center_Distance(line_img[i], line_idx[i * 2 : i * 2 + 2]))
Dis_Avg = sum(dis_avg)/len(dis_avg)
print(Dis_Avg)

# word_img = Word_Split(line_img[0], line_idx[0 : 2], 0)
for i in range(len(line_img)):
    word_img = Word_Split(line_img[i], line_idx[i * 2 : i * 2 + 2], i, Dis_Avg)



# 현재 코드의 문제점
# '(', ')' 추출 못함...
# 해결방안: 글자의 가로 길이의 평균을 구한후 길이가 평균의 몇 % 보다 작거나 크면 글자간 Combine Pass

'Coding > Image' 카테고리의 다른 글

Image hashing  (0) 2021.05.07
HOG & SVM  (0) 2021.05.07
Text Skew Correction  (0) 2021.05.07
Object Detection Terminology Overview  (0) 2021.05.06
IoU (Intersection over Union)  (0) 2021.05.06

관련글 더보기