Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

이미지 프로세싱 in Python Open Source - PYCON KOREA 2020

317 views

Published on

파이썬의 이미지 프로세싱은 Computer Vision과 Deep Learning 연구자들과 개발자들이 많이 사용하고 있으며,
라이브러리 생태계는 지속적으로, 발전하고 있고 요구되고 있습니다.

하지만, 여전히 오픈 소스 생태계에서 제공되는 라이브러리들의 기능으로는 섬세한 이미지 편집을 지원하지 못합니다.
즉 앞으로도 꾸준히 파이썬 이미지 프로세싱 라이브러리의 생태계는 기여와 발전이 필요합니다.

저는 유연하고 자유로운 이미지 편집을 파이썬에서 수행하기 위해 Pillow와 Wand, OpenCV를 병행하여 사용하였으며,
때로는 지원하지 않는 기능이나 버그로 인해 오픈 소스 생태계의 코드를 직접 수정하고 기여한 적이 있습니다.
이를 위해서 이미지 프로세싱의 깊은 이해를 바탕으로, 이미지 프로세싱 코드를 직접 다루어야 했습니다.

오늘 여러분에게 제가 겪었던 경험과 시행착오들을 짧고 간결하게 정리하여 공유드리고자 합니다.
저의 경험을 양분으로 여러분에게 어떤 인사이트가 되었으면 좋겠습니다.

Published in: Technology
  • Be the first to comment

이미지 프로세싱 in Python Open Source - PYCON KOREA 2020

  1. 1. 이미지 프로세싱 in Python Open Source 한성민 Gopher
  2. 2. Index • Image Processing • Open Source • Contribution
  3. 3. Image Processing
  4. 4. 좌측에 고양이 이미지가 있습니다. 이미지 프로세싱이란
  5. 5. 이 이미지는 RGB 형식이고 컴퓨터는 이것을 바이트 단위로 매트릭스(Matrix)라고 부르는 배열 형태로 저장합니다. 이미지 프로세싱이란 (166, 219, 216) (176, 176, 152) (150, 140, 111) (147, 131, 112) (135, 173, 159) (199, 160, 127) (199, 172, 155) (209, 198, 189) (131, 141, 115) (180, 142, 108) (173, 153, 150) (204, 189, 176) (95, 144, 134) (125, 127, 107) (154, 142, 132) (187, 169, 154)
  6. 6. 이미지 프로세싱이란 우리는 다양한 수식을 이용하여 이미지를 변환(Transformation)할 수 있고 이것을 이미지 프로세싱(Image Processing)이라 부릅니다. 𝟏 𝟏 𝟏 𝟎 𝟎𝟎 𝟎 𝟎 𝟎 𝟏 𝟏 𝟏 𝑿 𝒀𝟎 𝟎 𝟎 𝟎 𝑾 𝑯 𝟏 𝟎 𝟎𝟎 𝟎 𝟎 𝟎 Original Translate Scale 𝒄𝒐𝒔𝜽 𝟏 𝟎 𝟎 𝟎 𝟎 𝒄𝒐𝒔𝜽 𝒔𝒊𝒏𝜽 −𝒔𝒊𝒏𝜽 Rotate
  7. 7. 동영상 처리 이미지 프로세싱이란 이미지 프로세싱은 다양한 종류가 있으며 사진, 그림, 동영상에 따라서 이미지 프로세싱이 여러 갈래로 나눠집니다. Image Processing 사진, 그림 처리 기하학적 변환 색조화, 양자화, 색변환 패턴 인식, 특징 검출 압축 노이즈 제거 영상 분할, 접합
  8. 8. 이미지 프로세싱이란 오늘은 그 중에서 사진, 그림과 같은 2차원 데이터의 기하학적 변환에 대해서 얘기하고자 합니다. 기하학적 변환
  9. 9. 기하학적 변환 연산 기하학적 변환이란 이미지의 크기나 위치 변경, 혹은 회전 등의 변화를 가하는 것으로 어파인(Affine) 변환 혹은 원근(Perspective) 변환 등이 있습니다. 이동 (Translate) 회전 (Rotate)
  10. 10. from PIL import Image img = Image.open('./cat.jpg') x = 100 y = 50 a = 1 b = 0 c = -x # move the img 100 to the right d = 0 e = 1 f = y # move the img 50 to the up translate = img.transform(img.size, Image.AFFINE, (a, b, c, d, e, f)) translate.show() Pillow Affine Translate 𝟏 𝟏 𝟏 𝑿 𝒀𝟎 𝟎 𝟎 𝟎 Translate
  11. 11. from PIL import Image import math img = Image.open('./cat.jpg') angle = math.radians(45) # 45 degree a = math.cos(angle) b = math.sin(angle) c = 0 d = -math.sin(angle) e = math.cos(angle) f = 0 rotate = img.transform(img.size, Image.AFFINE, (a, b, c, d, e, f), resample=Image.BILINEAR) rotate.show() 𝒄𝒐𝒔𝜽 𝟏 𝟎 𝟎 𝟎 𝟎 𝒄𝒐𝒔𝜽 𝒔𝒊𝒏𝜽 −𝒔𝒊𝒏𝜽 Rotate Pillow Affine Rotate
  12. 12. def _create_coeff( xyA1, xyA2, xyA3, xyA4, xyB1, xyB2, xyB3, xyB4): A = np.array([ [xyA1[0], xyA1[1], 1, 0, 0, 0, -xyB1[0] * xyA1[0], -xyB1[0] * xyA1[1]], [0, 0, 0, xyA1[0], xyA1[1], 1, -xyB1[1] * xyA1[0], -xyB1[1] * xyA1[1]], [xyA2[0], xyA2[1], 1, 0, 0, 0, -xyB2[0] * xyA2[0], -xyB2[0] * xyA2[1]], [0, 0, 0, xyA2[0], xyA2[1], 1, -xyB2[1] * xyA2[0], -xyB2[1] * xyA2[1]], [xyA3[0], xyA3[1], 1, 0, 0, 0, -xyB3[0] * xyA3[0], -xyB3[0] * xyA3[1]], [0, 0, 0, xyA3[0], xyA3[1], 1, -xyB3[1] * xyA3[0], -xyB3[1] * xyA3[1]], [xyA4[0], xyA4[1], 1, 0, 0, 0, -xyB4[0] * xyA4[0], -xyB4[0] * xyA4[1]], [0, 0, 0, xyA4[0], xyA4[1], 1, -xyB4[1] * xyA4[0], -xyB4[1] * xyA4[1]], ], dtype=np.float32) B = np.array([xyB1[0], xyB1[1], xyB2[0], xyB2[1], xyB3[0], xyB3[1], xyB4[0], xyB4[1]], dtype=np.float32) return linalg.solve(A, B) img = Image.open('./cat.jpg') coeff = _create_coeff( (0, 0), (img.width, 0), (img.width, img.height), (0, img.height), (-200, 0), (img.width + 200, 0), (img.width, img.height), (0, img.height), ) perspective = img.transform(img.size, Image.PERSPECTIVE, coeff, resample=Image.BILINEAR) perspective.show() Pillow Affine Perspective
  13. 13. 색상 혼합(Blend) 어파인과 원근 변환 외에도 색상 혼합과 같은 방법을 이미지에 적용할 수 도 있습니다. 대표적으로 두 이미지의 색상을 혼합하는 블랜딩(Blending)이 있습니다.
  14. 14. Example Pillow from PIL import Image im1 = Image.open('./cat.jpg') im2 = Image.open('./dog.jpg') im3 = Image.blend(im1, im2, 0.5) im3.show() Im1, im2 블랜딩 대부분의 이미지 라이브러리는 블랜딩을 지원합니다.
  15. 15. Output 그렇다면, 이미지 라이브러리는 어떤 원리로 동작할까요?
  16. 16. Open Source
  17. 17. 코드 설계 + + + + + 로드 호출 C 바인드 변환 CPU 매트릭스 연산 파이썬에서 이미지 처리는 대부분 그림과 같이 동작합니다.
  18. 18. 파이썬 이미지 라이브러리 구조 Python Interface Python Bind C lang Compiled Libs 이미지 프로세스는 대부분 컴퓨터 성능을 많이 요구하기 때문에, C 언어로 작성하고 파이썬에 바인딩하여 사용합니다.
  19. 19. 파이썬 바인딩 파이썬은 C 바인딩을 통해 C에서 작성된 함수를 실행 가능하며, 이를 통해 복잡한 연산을 빠르게 실행할 수 있습니다. process.py binding.c호출 libImage.c libImage.h
  20. 20. 연산 방식 def blend(im1, im2, alpha): im1.load() im2.load() return im1._new(core.blend(im1.im, im2.im, alpha)) cat.jpg dog.jpg src/Image.py Imaging ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) { Imaging imOut; int x, y; ... if (alpha >= 0 && alpha <= 1.0) { /* Interpolate between bands */ for (y = 0; y < imIn1->ysize; y++) { UINT8* in1 = (UINT8*) imIn1->image[y]; ... for (x = 0; x < imIn1->linesize; x++) { out[x] = (UINT8) ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); } } } else { /* Extrapolation; must make sure to clip resulting values */ for (y = 0; y < imIn1->ysize; y++) { UINT8* in1 = (UINT8*) imIn1->image[y]; ... for (x = 0; x < imIn1->linesize; x++) { ... } } } return imOut; }src/libImaging/Blend.c 파이썬에서 C로 작성된 함수 호출
  21. 21. Python C Bind try: from . import _imaging as core if __version__ != getattr(core, "PILLOW_VERSION", None): raise ImportError( ... ) except ImportError as v: ... raise static PyObject* _blend(ImagingObject* self, PyObject* args) { ImagingObject* imagep1; ImagingObject* imagep2; double alpha; alpha = 0.5; if (!PyArg_ParseTuple(args, "O!O!|d", &Imaging_Type, &imagep1, &Imaging_Type, &imagep2, &alpha)) { return NULL; } return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image, (float) alpha)); } src/Image.py src/_imaging.c 예를 들어, Pillow는 core를 통해 C 함수를 호출합니다.
  22. 22. Python C Bind # # core library files = ["src/_imaging.c"] for src_file in _IMAGING: files.append("src/" + src_file + ".c") for src_file in _LIB_IMAGING: files.append(os.path.join("src/libImaging", src_file + ".c")) libs = self.add_imaging_libs.split() defs = [] if feature.jpeg: libs.append(feature.jpeg) defs.append(("HAVE_LIBJPEG", None)) ... if (sys.platform == "win32“ and ...): defs.append(("PILLOW_VERSION", '""%s""' % PILLOW_VERSION)) ... exts = [(Extension("PIL._imaging", files, libraries=libs, define_macros=defs))] setup.py
  23. 23. Python C Bind # # core library files = ["src/_imaging.c"] for src_file in _IMAGING: files.append("src/" + src_file + ".c") for src_file in _LIB_IMAGING: files.append(os.path.join("src/libImaging", src_file + ".c")) libs = self.add_imaging_libs.split() defs = [] if feature.jpeg: libs.append(feature.jpeg) defs.append(("HAVE_LIBJPEG", None)) ... if (sys.platform == "win32“ and ...): defs.append(("PILLOW_VERSION", '""%s""' % PILLOW_VERSION)) ... exts = [(Extension("PIL._imaging", files, libraries=libs, define_macros=defs))] C 라이브러리 바인드 setup.py
  24. 24. Python C Bind # # additional libraries ... tk_libs = ["psapi"] if sys.platform == "win32" else [] exts.append( Extension( "PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"], include_dirs=["src/Tk"], libraries=tk_libs, ) ) exts.append(Extension("PIL._imagingmath", ["src/_imagingmath.c"])) exts.append(Extension("PIL._imagingmorph", ["src/_imagingmorph.c"])) self.extensions[:] = exts build_ext.build_extensions(self) setup.py
  25. 25. Python C Bind # # additional libraries ... tk_libs = ["psapi"] if sys.platform == "win32" else [] exts.append( Extension( "PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"], include_dirs=["src/Tk"], libraries=tk_libs, ) ) exts.append(Extension("PIL._imagingmath", ["src/_imagingmath.c"])) exts.append(Extension("PIL._imagingmorph", ["src/_imagingmorph.c"])) self.extensions[:] = exts build_ext.build_extensions(self) setup.py 의존 라이브러리 빌드
  26. 26. Pillow Structure FreeType LibImaging Libjpeg libraqm harfbuzz libimagequant
  27. 27. Pillow Structure FreeType LibImaging Libjpeg libraqm harfbuzz libimagequant 폰트 랜더링 이미지 처리 JPG 연산 텍스트 레이아웃 계산 폰트 글리프 랜더링 RGBA / 팔레트 변환
  28. 28. ImageMagick 개발사 출시일 언어 라이센스 ImageMagick Studio LLC 1990년 8월 1일 C 언어 ImageMagick License
  29. 29. Wand from wand.image import Image from wand.display import display with Image(filename='cat.jpg') as img: print(img.size) for r in 1, 2, 3: with img.clone() as i: i.resize(int(i.width * r * 0.25), int(i.height * r * 0.25)) i.rotate(90 * r) i.save(filename='cat-{0}.jpg'.format(r)) display(i) 파이썬 라이브러리 Wand를 통해 ImageMagick을 사용할 수 있습니다.
  30. 30. Trending 2009년 하반기 이후로 OpenCV가 유행 추세 OpenCV는 컴퓨터 비전 및 물체 인식 등 다양한 기능을 추가로 제공
  31. 31. Contribution
  32. 32. Workflow PR (Pull Request) Jenkins pr-head trigger Lint error Blocking merge Fix commit Jenkins pr-head trigger Pass Request changes Fix commit Blocking merge Jenkins pr-head trigger Pass Approve Merge Merge Opened
  33. 33. 포럼을 통한 버그 제보
  34. 34. 포럼을 통한 버그 제보 ImageMagick 버그 포토샵, MS 워드 등 다른 프로그램 동작
  35. 35. ImageMagick PR
  36. 36. ImageMagick PR 밑줄 어파인 연산의 조정으로 위치 개선 폰트 색상과 동일한 밑줄 색상 적용
  37. 37. ImageMagick PR annotate_info->affine.tx=offset.x; annotate_info->affine.ty=offset.y; pixel=annotate_info->fill; if (annotate_info->stroke.alpha != TransparentAlpha) pixel = annotate_info->stroke; (void) QueryColorname(image,&pixel,AllCompliance,color,exception); (void) FormatLocaleString(primitive,MagickPathExtent,"stroke %s " "stroke-width %g " "line 0,0 %g,0",color,(double) metrics.underline_thickness,(double) metrics.width); ... annotate_info=DestroyDrawInfo(annotate_info); annotate=DestroyDrawInfo(annotate); textlist=(char **) RelinquishMagickMemory(textlist); text=DestroyString(text); return(status); 글자와 동일한 밑줄 색상 처리 MagickCore/annotate.c
  38. 38. FT_Face face; ... metrics->bounds.y1=metrics->descent; metrics->bounds.x2=metrics->ascent+metrics->descent; metrics->bounds.x1=0.0; metrics->bounds.y1=metrics->descent; metrics->bounds.x2=metrics->ascent+metrics->descent; metrics->bounds.y2=metrics->ascent+metrics->descent; metrics->underline_position=face->underline_position*(metrics->pixels_per_em.x/face->units_per_EM); metrics->underline_thickness=face->underline_thickness*(metrics->pixels_per_em.x/face->units_per_EM); ... ImageMagick PR MagickCore/annotate.c FreeType으로 부터 밑줄 크기, 위치 계산
  39. 39. convert -size 320x120 xc:lightblue -draw "fill tomato circle 250,30 310,30 fill limegreen circle 55,75 15,80 font Candice font-size 72 decorate UnderLine fill dodgerblue stroke navy stroke-width 2 translate 10,110 rotate -15 text 0,0 ' Anthony '" draw_mvg.gif Test with ImageMagick ImageMagick 명령어로 동작 확인
  40. 40. Test with Python Wand 파이썬 Wand에서 테스트 from wand.color import Color from wand.drawing import Drawing from wand.image import Image with Image(width=320, height=100, background=Color('lightblue')) as image: with Drawing() as draw: draw.font = 'Candice' draw.font_size = 72.0 draw.text_decoration = 'underline' draw.fill_color = Color('black') draw.text(28, 68, 'Anthony') draw(image) image.save(filename='test.jpg')
  41. 41. Thank you!

×