Python 으로 이미지에 텍스트를 찍어보자

파이선으로 이미지에 워터마크 넣는 코드 실행 화면

이전까진 블로그에 이미지 올리기 전에 워터마크 작업과 이미지 크기를 수작업으로 했는데 워드프레스가 업그레이드(?)되면서 사이즈 별로 자동 생성하기 때문에 워터마크 작업만 하면됩니다.

물론 PHP로 이미지 등록시 자동으로 워터마크를 박을 수 있겠지만 아직 할줄 모르기 때문에 업로드할 이미지를 폴더에 몰아놓고 실행하면 자동으로 워터마크를 박아주는 앱을 하나 만들어 봤습니다.

파이썬 3.8에서 작업함

import sys
from PIL import Image 
from PIL import ImageDraw 
from PIL import ImageFont
import re
import os
import errno


################################################################################
text_Watermark = 'Linsoo.co.kr `'
text_Font = './Goyang.ttf'

################################################################################
#확장자 찾는 정규식
re_findEXT = re.compile('.+.(w+)?$')

#파일 이름만 찾는 정규식 (.+[\/](w+)?..+$)
#파일 이름+확장자 까지 구하는 정규식
re_findFileName = re.compile('.+[\/]([^\/:*?"<>|]+.[^\/:*?"<>|]+)?$')
################################################################################


################################################################################
#이미지 컨버트 함수
def convertImage(srcFilePath, outputPath):
    
    extName = ''        #확장자
    srcFileName = ''    #원본 파일이름
    dstFileName = ''    #저장할 파일이름
    dstFilePath = ''    #저장할 경로(파일명 포함)

    #확장자 못찾으면 패스    
    match = re_findEXT.match(srcFilePath)
    if match == None:
        return None
    else:
        extName = match.group(1)
    
    match = re_findFileName.match(srcFilePath)
    if match ==  None:
        print('파일 이름 못찾음')
        return None
    else:
        srcFileName = match.group(1)
        dstFileName ='linsoo_'+srcFileName
        dstFilePath = outputPath+'/'+dstFileName
        #dstFilePath = srcFilePath.replace(srcFileName, dstFileName)
        
  
   

    ################################################################################
    #여기부터 이미지 컨버팅

    #워터마크 텍스트 색상
    colorText = (255,255,255,255)
    #아웃라인 색상
    colorOutline = (128,128,128,255)
    
    #그림자 색상
    colorShadow = (0,0,0,90)

    #그림자 위치
    shadowX = 3
    shadowY = 2

    #워터마크 폰트크기 비율 (이미지 크기의 몇%를 차지 하는가)
    waterMarkHeightRatio = 0.071

    ###########################################################################
    imgSrc= Image.open(srcFilePath).convert("RGBA")
    imgOutput = Image.new( "RGBA", imgSrc.size) 
    # #워터마크 폰트 크기 이미지 높이에 비례한다.
    fontSize = round(imgSrc.height * waterMarkHeightRatio)

    #아웃라인 두께
    outlineAmount = round(fontSize*0.013)
    #외곽선 굵기가 0 나오면 1 줌
    outlineAmount = 1 if outlineAmount < 1 else outlineAmount
    
    
    
    
    fontWatermark = ImageFont.truetype(font=text_Font, size = fontSize)
    fontWidth,fontHeight = fontWatermark.getsize(text_Watermark)

    #워터마크 이미지와 원본이미지와 떨어짐 간격
    offsetX = int(imgSrc.width * 0.01)
    offsetY = int(imgSrc.height * 0.02)

    #워터마크 x위치
    posWatermarkStartX = imgSrc.width - fontWidth - offsetX
    posWatermarkStartY = imgSrc.height - fontHeight - offsetY


    ###########################################################################
    #그림자
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX - outlineAmount +shadowX, posWatermarkStartY +shadowY ), text_Watermark,fill=colorShadow, font=fontWatermark) 
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX + outlineAmount +shadowX, posWatermarkStartY +shadowY), text_Watermark,fill=colorShadow, font=fontWatermark) 
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX +shadowX, posWatermarkStartY - outlineAmount +shadowY), text_Watermark,fill=colorShadow, font=fontWatermark) 
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX +shadowX, posWatermarkStartY + outlineAmount +shadowY), text_Watermark,fill=colorShadow, font=fontWatermark) 

    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX - outlineAmount +shadowX, posWatermarkStartY - outlineAmount +shadowY), text_Watermark,fill=colorShadow, font=fontWatermark) 
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX + outlineAmount +shadowX, posWatermarkStartY - outlineAmount +shadowY), text_Watermark,fill=colorShadow, font=fontWatermark) 
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX - outlineAmount +shadowX, posWatermarkStartY + outlineAmount +shadowY), text_Watermark,fill=colorShadow, font=fontWatermark) 
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX + outlineAmount +shadowX, posWatermarkStartY + outlineAmount +shadowY), text_Watermark,fill=colorShadow, font=fontWatermark) 
    # 그림자용 메인 텍스트
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX+shadowX, posWatermarkStartY+shadowY ), text_Watermark,fill=colorShadow, font=fontWatermark)
    ################################################################################################################################

    #외곽선
    #left
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX - outlineAmount, posWatermarkStartY ), text_Watermark,fill=colorOutline, font=fontWatermark) 
    #right
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX + outlineAmount, posWatermarkStartY ), text_Watermark,fill=colorOutline, font=fontWatermark) 
    #up
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX, posWatermarkStartY - outlineAmount ), text_Watermark,fill=colorOutline, font=fontWatermark) 
    # #down
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX, posWatermarkStartY + outlineAmount ), text_Watermark,fill=colorOutline, font=fontWatermark) 

    #left up
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX - outlineAmount, posWatermarkStartY - outlineAmount ), text_Watermark,fill=colorOutline, font=fontWatermark) 
    # #right up
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX + outlineAmount, posWatermarkStartY - outlineAmount ), text_Watermark,fill=colorOutline, font=fontWatermark) 
    # #left down
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX - outlineAmount, posWatermarkStartY + outlineAmount ), text_Watermark,fill=colorOutline, font=fontWatermark) 
    # #right down
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX + outlineAmount, posWatermarkStartY + outlineAmount ), text_Watermark,fill=colorOutline, font=fontWatermark) 

    #메인 텍스트
    ImageDraw.Draw(imgOutput).text( (posWatermarkStartX, posWatermarkStartY ), text_Watermark,fill=colorText, font=fontWatermark) 


    out = Image.alpha_composite(imgSrc, imgOutput)

    # 파일 옵션에 관한건 아래 링크를 참고
    # https://pillow.readthedocs.io/en/5.1.x/handbook/image-file-formats.html
    #파일을 저장한다.
    if(extName.lower() == 'png'):
        out.convert('RGB').save(dstFilePath, 'png')
    elif(extName.lower() == 'jpg'):
        out.convert('RGB').save(dstFilePath, 'jpeg',quality=95,optimize=True)

    print('-----------------------------------------------------------------')
    print(srcFilePath +' ==> '+dstFilePath)
    print("ImgWidth: "+str(imgSrc.width)+" ImgHeight: "+ str(imgSrc.height)+" FontSize: "+ str(fontSize)+" OutlineAmount: "+ str(outlineAmount))
################################################################################
#특정 폴더 경로안에 파일 리스트 가져옴
regex = re.compile('^.+.(jpg|png|bmp)$', re.IGNORECASE)

def convertFolder(srcPath, outputPath):
    outputPath = os.path.abspath(outputPath)
    try:
        if not(os.path.isdir(outputPath)):
            os.makedirs(os.path.join(outputPath))
    except OSError as e:
        if e.errno != errno.EEXIST:
            print("Failed to create directory!!!!!")
        raise



    try:
        filenames = os.listdir(srcPath)
        for filename in filenames:
            full_filename = os.path.join(srcPath, filename)
            
            mo = regex.search(full_filename)
            if mo != None:
                if os.path.isfile(mo.group()):
                    filePath = mo.group(0)
                    convertImage(filePath, outputPath)
    except PermissionError:
        pass

################################################################################
#실행부분
convertFolder('./', './output')

print('-----------------------------------------------------------------')
var = input ('엔터키를 누르면 종료합니다.')

Comments

답글 남기기

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