[python] Python PIL을 이용한 ImageDraw

글쓴이 Engineer Myoa 날짜

심심하(면 안되는데– )던 찰나에 한 분이 개발자 톡방에 문의를 남겨주셨다.

보자마자 이전에 본 PIL과 matplotlib중에 어떤 걸 사용해볼까 하면서 매우 들뜬상태로 집에 왔다.

내가 제일먼저 떠올랐던 구상은 이런것이었고…

역시나 요구사항도 비슷한 부분이었다.. 이미지 프로세싱이후 정상적으로 처리가 되었는지 그에대한 result를 시각화했으면 좋겠다, 라고 하면서

몇만장정도 존재하는데 일괄적으로 적용해야하는데 도움좀 달라하시더라

 

먼저 PIL로는 numpy쓰기가 귀찮아서 Image 불러들이는 용도로 사용하려고 했으나..

http://pillow.readthedocs.io/en/3.1.x/reference/ImageDraw.html

친절하게 ImageDraw에 대한 레퍼런스를 자세히 소개해주어 이를 참고했다.

 

1차 테스트 작업 결과물

 

im = Image.open("c:\\cpp\\test.png")
draw = ImageDraw.Draw(im)
draw.rectangle([(0,0), (100,100)], outline="red")
im.show()

맘에는 들었으나 rectangle을 사용하니 두께 설정이 안되더라..

참고로 fill이나 outline에는 꼭 명사형 색을 입력할 필요없고 “#FF0000″과 같은 색상코드를 입력해도 작동한다.

draw.line(xy, fill=None, width=0)

을 이용하여 선을 4개로 그려봤다.

width를 두껍게 설정하면 발생하는 문제도 있는데 이는 추후에 해결하도록하고..

(4개의 vertex가 정확하지 않은 4각형이 나온다)

 

2차 테스트 결과물

x1,y1, x2,y2 에 적절한 좌표를 주고 for문을 돌렸다.

여러개의 width=1 사각형을 그려마치 두께가 두꺼운 사각형인 것처럼 보이게

for i in xrange(width):
    draw.rectangle( [(x1-i, y1-i), (x2+i, y2+i) ] , outline = "red")

 

오오 완전 마음에 든다.

하지만 이는 4번이상 for문을 굴리게되면 line으로 그리는 것보다 비효율적일 터..

근본적인 line을 해결해보자

draw.line([(x1+width,y1+width), (x2-width,y1+width),
           (x2-width,y2-width), (x1+width, y2-width),
           (x1+width,x1+width)],
          width= 8, fill="red")

오, 얼추 마음에 드는 결과물이 나왔다.

여전히 vertex가 약간 어긋나있지만 이 정도면 만족할 수 있는 정도다.

+-값을 보정해준다면 더 그럴듯한 이미지가 나올것 같았다.

 

또한 기본적으로 width는 중심부를 기준으로 spread될 것..

이왕 line으로 처리할 거면 rectangle을 그리려고하는 범위를 line이 가리면 안된다고 생각한다.

 

3차 테스트 결과물

파란영역은 기존 Line을 쭉 이어서 그렸을때,

빨간영역은 그에 대해 영역보정과 vertex위치 보정을 감행하였다.

 

 

최종적으로는 클래스로 만들어 대량처리에 있어서 인스턴스를 사전에 할당해놓고 프로세싱 할 수 있도록 코드를 수정하였다.

 

# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw



class DrawImageHandler():

    COLOR = "#FF0000"


    def __init__(self):
        self.im = None
        self.draw = None

    def getImage(self, path):
        self.im = Image.open(path)
        self.draw = ImageDraw.Draw(self.im)

    def drawRect(self, (x1,y1), (x2,y2), width=4 ):
        if width < 4:
            self.__drawRectUnder4((x1,y1), (x2,y2), width)
        else:
            self.__drawRectOver4((x1,y1), (x2,y2), width)

        self.im.show()

    def __drawRectUnder4(self, (x1,y1), (x2,y2), width):
        for i in xrange(width):
            self.draw.rectangle([(x1-i, y1-i), (x2+i, y2+i)], outline=self.COLOR)

    def __drawRectOver4(self, (x1, y1), (x2,y2), width):
        originwidth = width
        width //=2
        width += 1

        self.draw.line([(x1-originwidth, y1-width), (x2+originwidth, y1-width)],
                       width = originwidth, fill=self.COLOR)
        self.draw.line([(x2+width, y1-originwidth), (x2+width, y2+originwidth)],
                       width = originwidth, fill=self.COLOR)
        self.draw.line([(x2+originwidth, y2+width), (x1-originwidth, y2+width)],
                       width = originwidth, fill=self.COLOR)
        self.draw.line([(x1-width, y2+originwidth), (x1-width, y1-originwidth)],
                       width = originwidth, fill=self.COLOR)

        """
        self.draw.line([(x1, y1), (x2, y1),
                        (x2, y2), (x1, y2),
                        (x1, x1)],
                       width= originwidth, fill="blue")
        """


    def flushing(self):
        pass

    def save(self):
        pass


path = "c:\\cpp\\test.png"

# 테스트 코드
if __name__ == "__main__":
    a = DrawImageHandler()
    a.getImage(path)
    a.drawRect((100,100),(400,400), width=8)



 

카테고리: 개발노트

1,247개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다