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

파이썬 watermarkApp

이전엔 블로그에 이미지를 올리기 전에 일반 이미지툴로 워터마크와 이미지 크기를 축소하는 작업을 했었는데 이제는 이미지 업로드 하면 사이즈별로 자동 생성하기 때문에 워터마크만 박으면 됨.

물론 php로도 이미지 업로드시 자동으로 워터마크 박는 법이 있을텐데 내가 모르니깐 업로드할 이미지들 모아놓고 실행파일 하나 실행하면 이미지 사이즈 별로 폰트 크기를 다르게 워터 마크 박는 app을 파이썬으로 작업.

아래에 코드 올림
이렇게 블로그에 글쓰는게 조금 더 편리해졌는데 자주 포스팅을 안하는게 문제네 ㅋㅋㅋ

파이썬 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 ('엔터키를 누르면 종료합니다.')

뭐 테스트 결과는 맨위에 이미지를 보면 알 수 있음 ㅋㅋㅋ

크리에이티브 커먼즈 라이선스Linsoo의 저작물인 이 저작물은(는)크리에이티브 커먼즈 저작자표시-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.

댓글 남기기

이메일은 공개되지 않습니다.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

This site uses Akismet to reduce spam. Learn how your comment data is processed.