import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
##--------이퀼라이징을 위한 라이브러리--------
#!pip install opencv-python
import cv2
##--------parameter 설정--------
import matplotlib
matplotlib.rcParams['figure.figsize'] = (3,2)
matplotlib.rcParams['figure.dpi'] = 150 ## 450 : 300Introduction | 그래프, 이미지 이퀼라이징
파이썬을 이용하여 간단한 그래프를 그려보고, 이미지를 이퀼라이징하는 방법을 알아보도록 하자.
해당 포스트는 전북대학교 통계학과 최규빈 교수님의 강의내용을 토대로 재구성되었음을 알립니다.
1. 사전작업
- 라이브러리 import
- 오늘 알아볼 함수들
plt.boxplot() ## 박스플롯 생성
np.random.randn() ## 정규분포 하 확률변수 추출(default : 표준정규분포에서 1개 추출)
np.random.seed() ## 시드 생성
plt.hist() ## 히스토그램 생성
cv2.imread() ## 이미지를 행렬로 읽어들임
plt.imshow() ## 행렬로 저장된 이미지를 시각화
cv2.equalizeHist() ## 히스토그램 이퀼라이징
!wget link ## 파일 다운로드(리눅스)
2. 플롯
A. Boxplot
y1 = [75,75,76,76,77,77,78,79,79,98]
y2 = [76,76,77,77,78,78,79,80,80,81]y1_frame = pd.DataFrame(y1)
y1_frame.describe()| 0 | |
|---|---|
| count | 10.000000 |
| mean | 79.000000 |
| std | 6.831301 |
| min | 75.000000 |
| 25% | 76.000000 |
| 50% | 77.000000 |
| 75% | 78.750000 |
| max | 98.000000 |
- 1반의 평균은 \(79\)
y2_frame = pd.DataFrame(y2)
y2_frame.describe()| 0 | |
|---|---|
| count | 10.00000 |
| mean | 78.20000 |
| std | 1.75119 |
| min | 76.00000 |
| 25% | 77.00000 |
| 50% | 78.00000 |
| 75% | 79.75000 |
| max | 81.00000 |
- 2반의 평균은 \(78.2\)이다.
그렇다면 1반(y1)과 2반(y2), 두 반을 지도하는 선생님 중 어떤 선생님이 우수할까?
아마도… : 평균을 중심으로 분석할 시 y1이 더 잘 지도했다고 판단할 수 있다.
반론 : 평균은 A반이 더 높으나, 편차또한 더 크다. 고득점을 받는 한 학생(outlier)을 제외하면 전체적으로 B반 학생들이 시험을 더 잘 보았다고 해석할 수 있다.
단순한 평균비교보다 학생들이 받은 점수의 분포를 비교하는 것이 중요.
따라서 그 분포를 알아보기 위해 Boxplot을 그려보자.
matplotlib로 boxplot 그리기
plt.boxplot(y1); ## 세미콜론을 붙이면 결과값만 출력한다._files/figure-html/cell-6-output-1.png)
plt.boxplot(y2);_files/figure-html/cell-7-output-1.png)
plt.boxplot([y1,y2]); ## 2차원의 리스트를 넣어 여러 개를 동시에 출력시킬 수도 있다.
##np.array([y1, y2]).shape ## > (2, 10)_files/figure-html/cell-8-output-1.png)
위처럼 하나의 outlier를 배제한다면, 나머지의 분포는 2반이 더 높게 위치함을 알 수 있다.
박스플롯의 장점 : 단순히 평균만 제공하는 것보다 데이터를 파악하고 직관을 얻기에 유용하다.
박스플롯이 이용되는 범위 : 초기 자료 분포를 파악하기 용이, 두 개 이상의 방법을 비교
B. Histogram
- 중심경향치(평균, 중앙값)만 가지고 집단을 비교할 순 없다.
이전의 자료도 결과론적으로 중앙값이 더 타당해 보이나, 이것을 근거로 B반이 공부를 더 잘했다는 주장도 비합리적이다.
- 단순 평균비교로 이러한 질문에 답을 하기 어려움.
- 박스플롯으로 전체분포를 파악해도 어떤 반이 공부를 더 잘한다는 기준을 잡기 애매함.
But!
특수한 경우에는 두 반 중에 누가 더 공부를 잘하냐는 질문에 명확히 대답할 수 있다.
np.random.seed(43052)
y1 = np.random.randn(10000) ## random.randn, standard normal distribution
y2 = np.random.randn(10000) + 0.5- 두 반의 성적은 모두 표준정규분포를 따르는데, 2반의 성적이 일괄적으로 0.5가 높은 상황
np.mean(y1), np.mean(y2)(-0.011790879905079434, 0.4979147460611458)
plt.boxplot([y1,y2]);_files/figure-html/cell-11-output-1.png)
- 분포의 모양이 거의 비슷한데, 중앙값(평균)이 2반이 더 높으므로 성적이 더 높다고 말할 수 있다. > 게다가 평균적으로 0.5점 정도 더 공부를 잘한다고 대답할 수 있다!
근데, 위와 같은 경우는 정규분포에서 뽑힌 랜덤샘플이라 분포의 모양이 같다고 하긴 했는데… 실제 데이터를 확인할 때는 박스플롯으로 하긴 어려워보인다.
따라서!
히스토그램을 그려 확인해보자
plt.hist(array, bins = int, range = list)
plt.hist([y1, y2], bins = 100);_files/figure-html/cell-12-output-1.png)
둘의 분포는 비슷하지만, 2반(주황색)이 조금 더 높은 수준에서 자리함을 알 수 있다.
3. Equalization
히스토그램이나 이미지를 눈으로 보기 쉽도록 이퀼라이징해보자!
이미지 자료 다운로드
#!pip install wget
import wget
wget.download("https://upload.wikimedia.org/wikipedia/commons/0/08/Unequalized_Hawkes_Bay_NZ.jpg")
img = cv2.imread("Unequalized_Hawkes_Bay_NZ.jpg")
##--------리눅스 환경 충족 시--------
##!wget https://upload.wikimedia.org/wikipedia/commons/0/08/Unequalized_Hawkes_Bay_NZ.jpg
##img = cv2.imread('https://upload.wikimedia.org/wikipedia/commons/0/08/Unequalized_Hawkes_Bay_NZ.jpg')
##!rm Unequalized_Hawkes_Bay_NZ.jpg
## 파일을 들여오고 인식한 뒤 삭제하는 코드이다.Requirement already satisfied: wget in c:\users\hollyriver\anaconda3\envs\ssk\lib\site-packages (3.2)
100% [............................................................................] 110895 / 110895
plt.imshow(img) ## image show_files/figure-html/cell-14-output-1.png)
plt.imshow()를 통해서 이미지를 가져왔다!
근데, img는 어떤 값으로 저장된 걸까?
A. 사실 이미지는 숫자열이었다!
_img1 = np.array([0,30,90,120,150,180,210,240,255]).reshape(3,-1) ## 3행 3열로 변경
_img1array([[ 0, 30, 90],
[120, 150, 180],
[210, 240, 255]])
plt.imshow(_img1, cmap = 'gray')
plt.colorbar()
plt.show()_files/figure-html/cell-16-output-1.png)
_img2 = np.array([0,20,40,60,80,100,120,140,160]).reshape(3,3)
_img2array([[ 0, 20, 40],
[ 60, 80, 100],
[120, 140, 160]])
plt.imshow(_img2, cmap = 'gray', vmin = 0, vmax = 255) ## vmin, vmax를 설정해주지 않으면 가장 큰 값이 max(white)가 된다
plt.colorbar()
plt.show()_files/figure-html/cell-18-output-1.png)
255에 가까울 수록 하얀색, 0에 가까울 수록 검정색인 이미지로 변환된 것을 볼 수 있다. 숫자만으로 이뤄진 행렬이 이미지가 된 것이다!
크게, 더 크게 해보자!
_img3 = np.concatenate([_img1,_img2], axis = 1) ## 열로 병합, default는 행으로 병합
_img3array([[ 0, 30, 90, 0, 20, 40],
[120, 150, 180, 60, 80, 100],
[210, 240, 255, 120, 140, 160]])
plt.imshow(_img3, cmap = 'gray')
plt.colorbar()
plt.show()_files/figure-html/cell-20-output-1.png)
B. RGB값을 더한 그림 그리기
- 먼저, RGB값에 해당하는 수를 각각 array로 지정해주자.
r = np.array(
[[255, 255, 255, 0, 0],
[255, 255, 255, 0, 0],
[255, 255, 255, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0]]
)
g = np.array(
[[ 0, 0, 255, 255, 255],
[ 0, 0, 255, 255, 255],
[ 0, 0, 255, 255, 255],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0]]
)
b = np.array(
[[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[255, 255, 255, 255, 255],
[255, 255, 255, 255, 255],
[255, 255, 255, 255, 255]]
)
z = np.array(
[[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0]]
)- 그리고 합쳐서 RGB값을 할당해준다.
red = np.stack([r,z,z], axis = -1)
green = np.stack([z,g,z], axis = -1)
blue = np.stack([z,z,b], axis = -1)temp = np.stack([r,g,b], axis = -1);temparray([[[255, 0, 0],
[255, 0, 0],
[255, 255, 0],
[ 0, 255, 0],
[ 0, 255, 0]],
[[255, 0, 0],
[255, 0, 0],
[255, 255, 0],
[ 0, 255, 0],
[ 0, 255, 0]],
[[255, 0, 255],
[255, 0, 255],
[255, 255, 255],
[ 0, 255, 255],
[ 0, 255, 255]],
[[ 0, 0, 255],
[ 0, 0, 255],
[ 0, 0, 255],
[ 0, 0, 255],
[ 0, 0, 255]],
[[ 0, 0, 255],
[ 0, 0, 255],
[ 0, 0, 255],
[ 0, 0, 255],
[ 0, 0, 255]]])
원소 하나 당 3개의 값이 할당된 것을 알 수 있다.
np.stack([], axis = -1)크기가 동일한 행렬들의 각 원소들을 리스트화 하여 원소로 저장한다.
plt.imshow(red+green+blue)
plt.show()_files/figure-html/cell-24-output-1.png)
R, G, B를 같은 비율로 섞으면 다시 흑백이미지가 된다.
arr2 = np.array([[10, 40], [80, 60]])
arr2array([[10, 40],
[80, 60]])
arr3 = np.stack([arr2, arr2, arr2], axis = -1) ## rgb값이 각각 동일
plt.imshow(arr3)
plt.show()_files/figure-html/cell-26-output-1.png)
img.shape ## 원소의 리스트 수가 3이라는 것으로 rgb가 포함되어 있음을 추측할 수 있음.(683, 1024, 3)
C. 히스토그램 이퀼라이징
그래서 이퀼라이징은 뭐냐고!
img에서 추출해온 행렬로 아래와 같은 히스토그램을 만들어보자.
r = img[:, :, 0] ## 첫 번째 원소
g = img[:, :, 1] ## 두 번째 원소
b = img[:, :, 2] ## 세 번째 원소plt.hist(r.reshape(-1),bins=255, range=[0,255])
plt.show()_files/figure-html/cell-29-output-1.png)
- 120~200 사이에 값이 몰려있음
- 120~200의 분포된 모양은 그대로 유지하면서 range를 0~255까지 늘린다면?
rr = cv2.equalizeHist(r)
gg = cv2.equalizeHist(g)
bb = cv2.equalizeHist(b)cv2 라이브러리의 equalizeHist() 사용하면 행렬의 모든 원소들의 분포 정도를 고르게(0~255) 바꾼다.
plt.hist(r.reshape(-1),bins=255, range=[0,255],label='befor');
plt.hist(rr.reshape(-1),bins=255,range=[0,255],label='after'); ## cv2.equalizeHist() 사용
plt.legend()
plt.show()_files/figure-html/cell-31-output-1.png)
그렇다면 이것을 응용하여 위에서의 이미지를 이퀼라이징하면?
- 이퀼라이징된 각 원소들을 다시 이어붙여 하나의 이미지로 만들어본다.
img2 = np.stack([rr,gg,bb], axis = -1) ## axis = -1 > z축(원소 내에서 확장)으로 추가
img2.shape(683, 1024, 3)
plt.imshow(img2)
plt.show()_files/figure-html/cell-33-output-1.png)
plt.imshow(np.concatenate([img,img2], axis = 1))
plt.show()_files/figure-html/cell-34-output-1.png)
이렇게, 이미지를 조금 더 구별하기 쉽도록 바꿀 수 있다.