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를 활용하여 이미지 히스토그램을 계산하는 방식에 대해서 배워봅시다.
히스토그램은 컴퓨터 비전의 거의 모든 분야에서 널리 사용됩니다:
그렇다면 히스토그램이 그렇게 유용한 이유는 뭘까요?
히스토그램은 어떤 데이터의 빈도 분포(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
결과:
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
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 |