티스토리 뷰
파이썬은 프로그래밍 언어로써 확고한 지위를 차지할 수 있었던 몇가지 이유가 있다.
기존 builtins 라이브러리에 추가로 서드 파티 즉 외부 라이브러리가 방대하고 강력한게 많다.
그러나 너무 많기 때문에 빛을 보지 못하고 잊혀지는 아까운 라이브러리 들도 있다.
또 한 한 영역에만 집중적으로 작업하는 프로그래머는 다른 유형의 작업을 위해 만들어진 라이브러리를 통해 얻을
수 있는 유익한 부분을 미쳐 모르는 경우가 있다.
[Pillow]
이미지 처리에 경험이 있다면 대부분의 파이썬 사용자들은 PIL(파이썬 이미지 라이브러리)에 익술 할 것이다.
그러나 PIL 은 제약이 많으며 업데이트가 잦지 않다는 단점이 있다.
필로우는 PIL 보다 더 사용하기 쉬우면서도, 최소한의 변경만으로 PIL 와 코드 호환성을 확보에 목표를 두고 있다.
네이티브 윈도우 이미징 기능과 파이썬의 Tcl/Tk 를 지원하는 Tkinter GUI 패키지를 사용하기 위한 확장이 되어 있다.
파이참에서 pillow 모듈을 검색하여 설치해 주자
from PIL import Image
그리고 모듈을 불러 온다.
패키지 설지는 pillow 검색해서 하면 된다.
from PIL import Image
img = Image.open("sample.jpg")
print(img.size, img.format)
img.show()
3줄만에 사이즈, 파일 포멧, 그리고 화면에 이미지를 띄울 수 있다.
굉장히 간결하다.
[color space]
색공간은 색 표시계를 3차원으로 표현한 공간 개념이다.
쉽게 말해서 색을 어떻게 표현하느냐 하는 것이다.
RGB
가산혼합, 즉 더해서 WHITE 가 되는 색 공간 을 뜻한다.
색의 삼원색인 red, green, blue 로 모든 색을 표현할 수 있다.
여기에 RGBA 는 A는 알파 즉 투과도를 덧붙인 것이다.
r,g,b 세가지로 모든 색을 표현한다.
CMYK
cmyk 색 공간은 인쇄과정에서 쓰이는 감산 혼합 방식으로
흰바탕에 네가지 잉크 조합으로 색을 나타내는 것을 말한다.
색을 혼합하면 명도가 낮아지기에 감산 혼합이라고 한다.
cmyk 는 인쇄에 쓰이는 4가지 색은 옥색(cyan), 자청색(magenta), 노랑(yellow), 검정(black)이다.
HSV
hsv 색공간은 색상(hue), 채도(saturation), 명도(value) 를 기준으로 색을 구성하는 방식이다.
감산이나 가산혼합보다 색상의 지정이 직관적이기 때문에 시각 예술에서 자주 쓰인다.
[pixel]
이미지는 2 차원 좌표에 어떤 색의 점이 있느냐로 구성된다.
따라서 점에는 좌표, 색상의 정보를 가지고 있다.
이것을 클래스로 만들면 다음과 같이 표현할 수 있다.
딕셔너리 키에 튜플을 넣어서 2차원 리스트 처럼 사용하자.
class Point:
def __init__(self, width, height):
self.info = [[(0, 0, 0) for _ in range(width)] for _ in range(height)]
def __setitem__(self, key, value):
self.info[key[0]][key[1]] = value
def __getitem__(self, item):
return self.info[item[0]][item[1]]
if __name__ == '__main__':
p = Point(3, 3)
print(p[1, 1])
p[1, 1] = (-1, -1, -1)
print(p[1, 1])
[gray]
이미지는 픽셀 레벨에서 이미지를 자유롭게 다룰 수 있다.
간단하게 r, g ,b 칼라 스페이스를 사용하는 포멧에서 gray scale 로 변경해 보자.
이미지의 사이즈를 먼저 파악하고 픽셀의 가로 세로 크기를 확인하자
사이즈를 알면 이중 for 문으로 모든 픽셀을 순회 하면서 변경 할 수 있다.
픽셀 하나는 칼라 스페이스가 "RGB" 로 생성했기 때문에 3개의 값을 가지고 있다.
0~255의 값을 가진다.
R, G , B 값이 모두 같으면 흑백이 되는것이다.
여기서 2중 포문은 굉장히 비용이 많은 연산임을 인지하자
def gray(ori):
dst = Image.new("RGB", ori.size, (0, 0, 0))
(width, height) = ori.size
pixels_dst = dst.load()
pixels_ori = ori.load()
for y in range(0, height):
for x in range(0, width):
color = pixels_ori[x, y]
aver = sum(color)//len(color)
pixels_dst[x, y] = (aver, aver, aver)
return dst
if __name__ == '__main__':
image = Image.open("./img/sample.jpg")
image.show()
img = gray(ori=image)
img.show()
[color detection]
원본 영상에서 원하는 색상만 찾아내는 것을 말한다.
특정색상의 범위를 지정해 준다.
특정 색을 지정해 주면 생각보다 필터가 잘 되지 않는다.
255*255*255 의 가지수 중에 특정 색을 지정하는 것은 빗겨갈 확률이 크기 때문에
범위로 지정하자.
모든 픽셀을 순회 하면서 해당 범위 안에 있는 색상만 추출해 보자
color wheel 에서 원하는 색상을 추출해 보자.
여기에는 모든 색이 다 있기 때문에 직관적으로 어떤색이 추출 되었는지 알 수 있다.
바이너리 이미지로 추출할 수도 있지만 보기 편하기 위해 오리지널 색상으로 표시해 봤다.
파란색을 추출하였다.
범위를 더 넓이면 영역이 넓어 질 것이다.
녹색을 추출해 봤다.
마지막으로 레드
[edge]
외곽선만 추출하는 알고리즘이다.
고주파 필터로 부를수도 있는데 이는 차이가 큰 픽셀들만 통과시키기 때문이다.
고주파: 픽셀간의 값의 차이가 큰 픽셀
저주파: 픽셀간의 값의 차이가 작은 픽셀
고주파는 즉 경계를 뜻하고 저주파는 같은 영역안에 있는걸 의미한다.
왼쪽의 원본 영상에서 경계선만 검출해서 바이너리 영상(참, 거짓)으로 표시하였다.
주파수 방향을 우측 하단으로 잡았기 때문에 해당 방향만 더 뚜렷하게 표시 되었다.
[mosaic]
이미지의 부분 혹은 전체에 마스크를 씌워서 픽셀을 통합 혹은 재배열 하여 원본의 픽셀의 정보를 추측하기 어렵게 한다.
물론 기존의 실루엣을 보인다.
원본에서 이미지 전체를 통합하여 재 생성하였다.
위치를 재배열 하지는 않아서 이미지가 축소된 느낌을 준다.
[cartoon]
근처 비슷한 픽셀들이 동일한 색상을 가지게 한다.
edge 와 반대의 개념일수 있는게 저주파 즉 비슷한 것들을 검출해 내기 때문이다.
재귀로 푸는건 좋지가 않다. 콜스택 1000 개가 넘어가기 때문이고
반복문으로 해결하는게 좋을 것이다.
원본영상 대비 점들이 비슷한 것들이 그룹화 된것을 확인 할 수 있다.
그룹은 마스크로 한것이 아니기 때문에 크기가 일정하지 않다.
interface.py
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import threading
from processing import *
class Buttons:
def __init__(self):
self.btns = list()
def add(self, title, func):
btn = QPushButton(title)
btn.clicked.connect(func)
self.btns.append(btn)
def __getitem__(self, item):
return self.btns[item]
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self._init_all()
def _init_all(self):
self._init_window()
self._init_buttons()
self._init_progress()
self._init_label()
def _center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def _init_window(self):
self.setGeometry(800, 200, 300, 300)
self.setWindowTitle("Image Processing v0.1")
self._center()
def _init_progress(self):
self.pbar = QProgressBar(self)
self.pbar.setRange(0, 99)
self.timer = QBasicTimer()
self.progress = 0
def _init_buttons(self):
self.btns = Buttons()
titles = ('FIle Open', 'Gray', 'Color Detection', 'Mosaic', 'Cartoon', 'Edge')
funcs = (self._btn_fileopen, self._btn_gray, self._btn_color_detection, self._btn_mosaic, self._btn_cartoon, self._btn_edge)
for title, func in zip(titles, funcs):
self.btns.add(title, func)
def _init_label(self):
self.label = QLabel()
layout = QVBoxLayout()
for widget in [*self.btns, self.label, self.pbar]:
layout.addWidget(widget)
self.setLayout(layout)
def _init_file(self):
self.file = None
def progress_do(self, step=1):
if self.progress+step < 100:
self.progress += step
def _progress_start(self):
self.progress = 0
self.pbar.setValue(self.progress)
self.timer.start(-100, self)
def timerEvent(self, e):
if self.progress < 100:
self.pbar.setValue(self.progress)
else:
self.pbar.reset()
def _work_start(self, func, *args, **kwargs):
self._progress_start()
t = threading.Thread(target=func, args=args, kwargs=kwargs)
t.daemon = True
t.start()
@staticmethod
def _get_color_range(color, value):
min_ = [c - value if c - value >= 0 else 0 for c in color]
max_ = [c + value if c + value < 256 else 256 for c in color]
return min_, max_
@staticmethod
def _get_color():
row = QColorDialog.getColor()
row = row.name()[1:]
color = [int("0x" + str(row[i:i+2]), 16) for i in range(0, 6, 2)]
return color
def _btn_fileopen(self):
fname = QFileDialog.getOpenFileName(self)
self.label.setText(fname[0])
self.file = fname[0]
def _btn_gray(self):
self._work_start(gray, self.file, gui=self)
def _btn_color_detection(self):
color = self._get_color()
value, ok = QInputDialog.getInt(self, 'Color Detection', 'Input color range', 30, 10, 100, 5)
if ok:
min_, max_ = MainWindow._get_color_range(color, value)
self._work_start(detect, self.file, gui=self, start=min_, stop=max_)
def _btn_mosaic(self):
value, ok = QInputDialog.getInt(self, 'Mosaic', 'Input mask size', 5, 5, 10, 1)
if ok:
self._work_start(mosaic, self.file, gui=self, size=value)
def _btn_cartoon(self):
value, ok = QInputDialog.getInt(self, 'Cartoon', 'Input threshold value', 25, 20, 100, 5)
if ok:
self._work_start(cartoon, self.file, gui=self, ts=value)
def _btn_edge(self):
value, ok = QInputDialog.getInt(self, 'Edge', 'Input threshold value', 100, 25, 255*3, 25)
if ok:
self._work_start(edge, self.file, gui=self, ts=value)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
processing.py
from PIL import Image
FORMATS = ('jpg', 'png')
def decorator(func):
def wrapper(file, *args, **kwargs):
try:
ori = Image.open(file)
except FileNotFoundError as e:
print(e)
except Exception as e:
print(e)
else:
dst = Image.new("RGB", ori.size, (0, 0, 0))
(width, height) = ori.size
pixels_dst = dst.load()
pixels_ori = ori.load()
ori.show()
return func(ori, *args, **kwargs, dst=dst, width=width, height=height, pixels_dst=pixels_dst, pixels_ori=pixels_ori)
return wrapper
@decorator
def detect(file, start, stop, maintain=True, gui=None, dst=None, width=None, height=None, pixels_dst=None, pixels_ori=None):
for y in range(0, height):
for x in range(0, width):
color = pixels_ori[x, y]
flag = all([s <= c < t for s, t, c in zip(start, stop, color)])
if flag:
pixels_dst[x, y] = pixels_ori[x, y] if maintain else (255, 255, 255)
if gui:
gui.progress_do(100 / height)
dst.show()
return dst
@decorator
def gray(file, gui=None, dst=None, width=None, height=None, pixels_dst=None, pixels_ori=None):
for y in range(0, height):
for x in range(0, width):
color = pixels_ori[x, y]
aver = sum(color)//len(color)
pixels_dst[x, y] = (aver, aver, aver)
if gui:
gui.progress_do(100 / height)
dst.show()
return dst
@decorator
def mosaic(file, gui=None, size=5, dst=None, width=None, height=None, pixels_dst=None, pixels_ori=None):
for y in range(0, height, size):
for x in range(0, width, size):
y_st = y - size if y-size >= 0 else 0
y_sp = y + size + 1 if y+size < height else height
x_st = x - size if x-size >= 0 else 0
x_sp = x + size + 1 if x+size < width else width
for yt in range(y_st, y_sp):
for xt in range(x_st, x_sp):
pixels_dst[xt, yt] = pixels_ori[x, y]
if gui:
gui.progress_do((100 / height) * size)
dst.show()
return dst
def _is_area(a, b, ts):
return all([abs(c_a - c_b) <= ts for c_a, c_b in zip(a, b)])
def _get_dif(a, b):
return sum([abs(c_a - c_b) for c_a, c_b in zip(a, b)]) // len(a)
def _paint(point, ori, dst, dif_map, xt, yt, ts):
if _is_area(point, ori[xt, yt], ts):
dif = _get_dif(point, ori[xt, yt])
if dif < dif_map[xt][yt]:
dst[xt, yt] = point
dif_map[xt][yt] = dif
def _chk_around(point, ori, dst, dif_map, ts, x, y, width, height):
for yt in range(y-1 if y-1 >= 0 else 0, y+2 if y+2 <= height else y):
for xt in range(x-1 if x-1 >= 0 else 0, x+2 if x+2 <= width else x):
_paint(point, ori, dst, dif_map, xt, yt, ts)
@decorator
def cartoon(file, gui=None, ts=40, dst=None, width=None, height=None, pixels_dst=None, pixels_ori=None):
for y in range(height):
for x in range(width):
pixels_dst[x, y] = pixels_ori[x, y]
dif_map = [[255 for _ in range(height)] for _ in range(width)]
for y in range(height):
for x in range(width):
c = pixels_dst[x, y]
_chk_around(c, pixels_ori, pixels_dst, dif_map, ts, x, y, width, height)
if gui:
gui.progress_do(100 / height)
dst.show()
return dst
def _chk_threshold(a, b, ts):
return sum([abs(c_a - c_b) for c_a, c_b in zip(a, b)]) > ts
def _filter(ori, dst, xt, yt, x, y, ts):
if _chk_threshold(ori[x, y], ori[xt, yt], ts):
dst[x, y] = (255, 255, 255)
def _pass_around(ori, dst, ts, x, y, width, height, step):
xt = x+1
yt = y+1
_filter(ori, dst, xt, yt, x, y, ts)
@decorator
def edge(file, gui=None, ts=100, dst=None, width=None, height=None, pixels_dst=None, pixels_ori=None):
step = 1
for y in range(height-step):
for x in range(width-step):
_pass_around(pixels_ori, pixels_dst, ts, x, y, width, height, step)
if gui:
gui.progress_do(100 / height)
dst.show()
return dst
if __name__ == '__main__':
# gray("./img/dog.jpg")
detect("./img/images.jpg", start=(0, 50, 0), stop=(100, 256, 100))
# cartoon("./img/dog.jpg", ts=20)
# edge("./img/dog.jpg", ts=100)
'python lecture > project' 카테고리의 다른 글
[python] 해시의 무결성 (0) | 2020.05.07 |
---|---|
[edu] 연락처 (class, 연산자 오버로드) (0) | 2020.03.16 |
[edu] sqlite3 import/export to csv (0) | 2019.02.07 |
[edu] 가위바위보 게임 (랜덤, 확률, 결과 예측) (0) | 2019.01.10 |
[edu] 계산기 테스트 자동화 (수식 자동생성) (0) | 2019.01.09 |
- Total
- Today
- Yesterday
- GIT
- 장고 플러스친구 자동응답
- wsgi
- PuTTYGen
- pycrypto
- 파이썬 독학
- admin.py
- 파이썬
- 플러스친구 자동응답
- 파이썬 입문
- 문서 비교
- 장고
- 장고 카톡 자동응답
- django
- Python
- 모바일 스킨 적용
- 면접답변
- chatbot
- 파이썬 강좌
- 면접정답
- virtualenv
- gitignore
- 모바일 테마 적용
- django chatbot
- 엑셀 비교
- gitlab
- 문과 코딩
- 이미지 비교
- 파이썬 프로그래밍
- Tistory
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |