카테고리 보관물: python

PythonMagick과 PIL을 이용한 PDF에서 이미지 추출

테스트 환경

  • 운영체제: Windows 7 64Bit
  • PythonVersion: 2.7

ImageMagick 설치

  1. ImageMagick 다운로드 [링크]
  2. ImageMagick 설치
  3. 중요 환경 변수 등록 (IMAGEMAGICK_HOME)

PythonMagick 설치

PIP 또는 easy_install 을 이용하여 설치 할 수 없기 때문에 수동으로 설치를 해야 하는 부분이 있습니다.

  1. PythonMagick‑0.9.10‑cp27‑none‑win32.whl 파일 다운로드 [링크] (운영체제가 64Bit여도 32Bit 파일을 다운로드)
  2. pip 를 이용하여 설치
C:UserscoozplzDownloads>pip install PythonMagick-0.9.10-cp27-none-win32.whl

테스트
파이썬 명령 프롬프트에서 import를 했을때 Module Not Found 가 표시되지 않으면 정상적으로 설치된 것입니다.

>>> import PythonMagick

GhostScript 설치

GhostScript가 없는 경우 Windows에서 실행하면 파일의 저장경로를 찾을 수 없다는 오류가 발생됩니다.

  1. GhostScript 32Bit 다운로드 [링크] (운영체제가 64Bit여도 32Bit 파일을 다운로드)
  2. GhostScript 설치
  3. 환경변수 등록 (GHOSTSCRIPT_HOME)

PIL 설치

> easy_install PIL

테스트
파이썬 명령 프롬프트에서 import를 했을때 Module Not Found 가 표시되지 않으면 정상적으로 설치된 것입니다.

>>> import Image

소스

import PythonMagick
import Image
import time

def loadPDFImage(pdfPath, outputFile):
    """
    PDF 파일을 로딩하여 이미지 파일로 저장한다.
    :param pdfPath  PDF 파일 경로
    :param outputFile   추출한 이미지 저장 경로
    """
    pdfImage = PythonMagick.Image()
    pdfImage.density("300")
    pdfImage.read(pdfPath)
    pdfImage.write(outputFile)


def splitImage(imageFileName, splittedImageSavingDir):
    """
    이미지 파일을 원하는 크기로 Split한 후 저장한다.
    :param imageFileName    원본 이미지의 경로
    :param splittedImageSavingDir   잘린 이미지를 저장할 상위 디렉토리
    """
    loadedImage = Image.open(imageFileName)
    for i in range(3):
        left = 256 + (952 * i) + (15 * i)
        right = 1212 + (952 * i) + (15 * i)
        cropImage = loadedImage.crop((left, 278, right, 1800))
        savingFileName = "%s_%d.jpg" % (time.strftime('%Y%m%d%H%M'), i)
        cropImage.save("%s/%s" % (splittedImageSavingDir, savingFileName))
        print "Successfully saved, %s" % savingFileName


if __name__ == '__main__':
    pdfPath = "D:/test.pdf"
    outputPath = "D:/test.jpg"
    splittedImageSavingDir = "D:/"
    loadPDFImage(pdfPath, outputPath)
    splitImage(outputPath, splittedImageSavingDir)

결과

원본 PDF
ORIGIN_IMAGE

잘린 이미지
SPLIT_IMAGE{max-width:320px}

Python 스크립트파일을 Exe로 변경

Install py2exe

> pip install py2exe

실행 스크립트

#execute_test.py
print 'HelloWorld'

Setup.py 스크립트

#setup.py
from distutils.core import setup
import py2exe, sys, os

sys.argv.append('py2exe')

options = {
    "bundle_files": 1,                    # create singlefile exe
    "compressed"  : 1,                    # compress the library archive
    "excludes"    : ["w9xpopen.exe"]    # we don't need this
}

setup(
    options = {'py2exe' : options},
    windows = [{'script': "merge_swf_helper.py"}],
    zipfile = None,
)

Cmd 창에서 빌드 명령 실행

> python setup.py install

결과 확인

명령을 실행한 폴더의 dist 디렉토리에 들어가면 생성된 exe 파일을 볼 수 있다.

[Python] Read excel(xlsx)

xlrd 를 이용한 엑셀 파일 읽기


기능

xlsx 파일에서 cell에 입력된 Text 데이터를 모두 출력한다. 출력 포맷은 excel 데이터와 동일하게 출력하도록 한다. (ROW별로 출력)

리소스정보

  • 입력: 컨텐츠가 포함된 xlsx 파일(resource.xlsx)

소스코드
#-*- coding: utf-8 -*-
# ================================================
# Author: coozplz@gmail.com
# Date: 2015. 08. 11
# Desc: 토플 앱 개발에 필요한 엑셀 파일 분석
# ================================================

import xlrd

def open_excel(path):
    '''
    엑셀 파일에 포함된 내용을 system.out으로 출력한다. 
    '''
    workbook = xlrd.open_workbook(path)

    # 엑셀 시트 개수 정보 출력
    print workbook.nsheets
    # 0번 시트정보를 조회한다.
    sheet = workbook.sheet_by_index(0)
    for row_num in range(sheet.nrows):
        row = sheet.row(row_num)
        cols = []
        for cell in row:
            if (cell.ctype == 1):
                cols.append(cell.value)
        print ','.join(cols)

#
# Main
#
if __name__ == '__main__':
    path = './word_end_0811.xlsx'
    open_excel(path)

[ST3] Python 개발환경 설정하기

Sublime Text 3(ST3) Python 개발환경 설정하기

맥에서 사용하는 Sublime Text 3 를 파이썬 개발 환경으로 사용하기 위한 설정에 대해 정리

ST3 다운로드 및 인스톨

다운로드

패키지 인스톨러 설치

다운로드

링크 생성

$ sudo ln -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl /usr/bin/subl

테마 다운로드

인스톨 패키지를 이용하여 설치.

  • Flatland

환경설정

사용자 설정

경로: Preferences > Settings – User

{
  "auto_complete": false,
  "auto_complete_commit_on_tab": true,
  "auto_match_enabled": true,
  "bold_folder_labels": true,
  "caret_style": "solid",
  "color_scheme": "Packages/Theme - Flatland/Flatland Dark.tmTheme",
  "detect_indentation": true,
  "draw_indent_guides": true,
  "ensure_newline_at_eof_on_save": true,
  "file_exclude_patterns": [
    "*.DS_Store",
    "*.pyc",
    "*.git"
  ],
  "find_selected_text": true,
  "fold_buttons": false,
  "folder_exclude_patterns": [],
  "font_face": "Menlo",
  "font_options": [
    "no_round"
  ],
  "font_size": 13,
  "highlight_line": true,
  "highlight_modified_tabs": true,
  "ignored_packages": [
    "Vintage"
  ],
  "indent_to_bracket": true,
  "line_padding_bottom": 0,
  "line_padding_top": 0,
  "match_brackets": true,
  "match_brackets_angle": false,
  "match_brackets_braces": true,
  "match_brackets_content": true,
  "match_brackets_square": true,
  "new_window_settings": {
    "hide_open_files": true,
    "show_tabs": true,
    "side_bar_visible": true,
    "status_bar_visible": true
  },
  "remember_open_files": true,
  "remember_open_folders": true,
  "save_on_focus_lost": true,
  "scroll_past_end": false,
  "show_full_path": true,
  "show_minimap": false,
  "tab_size": 2,
  "theme": "Flatland Dark.sublime-theme",
  "translate_tabs_to_spaces": true,
  "trim_trailing_white_space_on_save": true,
  "use_simple_full_screen": true,
  "vintage_start_in_command_mode": false,
  "wide_caret": true,
  "word_wrap": true
}

Python 환경 설정

  • 경로: Preferences > Settings – User 설정 파일 위치와 동일한 위치
  • 파일명: Python.sublime-settings
 
{
 "draw_white_space": "all",
 "auto_indent": true,
 "rulers": [
 79
 ],
 "smart_indent": true,
 "tab_size": 4,
 "trim_automatic_white_space": true,
 "use_tab_stops": true,
 "word_wrap": true,
 "wrap_width": 80
}

플러그인 설치

SideBarEnhancements
Anacoda
  • Lint 제거Preferences > Package Setting > Anaconda > User – Setting {“anaconda_lint” : false}
  • 단축키
    • Goto Definition: ctrl + alt + g
    • Find Usages : ctrl + alt + f
    • Get Documentation: ctrl + alt + d
SublimeLinter-pep8 and SublimeLinter-pyflakes
GitGutter
SublimeREPL
  1. 단축키 생성 방법
    1. SublimeREPL.sublime-build 파일 생성
      경로: ~/Application Support/Sublime Text 3/Packages/User
    2. SublimeREPL.sublime-build 설정 내용 추가
    3. {
          "target": "run_existing_window_command", 
          "id": "repl_python_run",
          "file": "config/Python/Main.sublime-menu"
      }
      
    4. command + b 빌드 결과 출력 여부 확인
  • 단축키
    • Goto Definition: ctrl + alt + g
    • Find Usages : ctrl + alt + f
    • Get Documentation: ctrl + alt + d
 

참조링크

[Python] 폴더내 파일명 일괄 변경

폴더내 파일명 일괄 변경


개발내용

플래시 컨텐츠 테스트를 위해 폴더내 파일명을 특정 패턴에 맞게 일괄로 변경해야 되는 이슈가 있어 개발.

개발환경

  • Language: Python 2.7.10
  • OS: Windows 7 64bit

소스코드
#-*- coding: utf-8 -*-
# ===============================================================
# Author: coozplz@gmail.com
# File: flash_korean_file_rename.py
# Date: 2015. 07. 14
# Desc: 플래시 샘플 컨텐츠들의 이름을 변경하기 위한 프로그램.
# ===============================================================

import os

def rename_flash_content(path, key, startIndex):
	'''
	플래시 샘플 컨테츠의 이름을 변경한다.
	:param path 루트 디렉토리 경로
	:param key 컨텐츠 구분값(명사: N, 형용사: A, 동사: V, 인사말:G)
	:param startIndex 컨텐츠 시작 번호
	'''
	print 'path=%s' % path
	minValue = i = startIndex;
	j = 1

	for item in os.listdir(root):
		# 파일명과 확장자를 분리한다.
		# item: 493-A023-c.swf
		# filename: 493-A023-c
		# file_extension: .swf
		filename, file_extension = os.path.splitext(item)

		if (file_extension != '.swf'):
			print 'invalid file name %s' % item
			continue;

		# item의 절대 경로를 구한다.
		fullpath = os.path.join(root, item)

		mod = j % 4
		suffix = 'a'
		if mod == 1:
		suffix = 'b'
		elif mod == 2:
		suffix = 'c'
		elif mod == 3:
		suffix = 'd'
		else:
		suffix = 'a'

		# 변경할 이름의 포맷을 정의한다.
		renamed_file_name = "%d-%s%03d_%s.swf" % (i, key, i - minValue + 1, suffix)
		if (key == 'NUM'):
			renamed_file_name = "%d-%s%02d_%s.swf" % (i, key, i - minValue + 1, suffix)
		#endif	
		if (j % 4 == 0):
			i = i+1
			j=j+1
			rename_full_path = os.path.join(root, renamed_file_name)
			os.rename(fullpath, rename_full_path)
			print '%s is rename to %s' % (item, renamed_file_name)
		#end - if
	#end - for	
if __name__ == '__main__':
	root = "D:/git/flash_korean_novice/resources/ai/verb"
	key = 'V'
	startIndex=321
	print root
	rename_flash_content(root, key, startIndex)
#end-if	

[Python] AndroidLint result to CSV

최근 개발 도구를 JetBrain 라인의 제품을 사용하고 있습니다.

Android는 AndroidStudio를 이용해서 하고 Java는 IntelliJ를 이용하고 Python은 PyCharm을 이용해서 하고 있습니다.

같은 회사의 제품을 사용하니 단축키를 동일하게 할 수 있어 좋습니다.
물론 Android Studio에서 Eclipse 단축키를 제공하기는 하지만 저는 그냥 단축키를 바꾸는 편을 선호 합니다.

예전에 PC방에 가도 단축키를 모두 바꿔서 적응하면 게임방을 옮길때마다 변경해줘야 하는 불편함이 있어서 몸에 벤 방식이 기본을 그대로 사용하고 있습니다. (많이 불편하면 변경을 합니다.)

잡설은 빼고… 어쨌든 정적 분석도구를 이용해서 분석한 후 Export를 하려고 하니 HTML, XML 두가지 방식을 지원합니다. XML로 저장 후 Excel로 읽을려고 하니 안될꺼 같아. 내용만 뽑아 CSV형태로 저장하는 방식으로 했습니다.

[Problem]
AndroidStudio에서 Lint를 이용해 분석된 XML 결과를 Excel로 보고싶다.

[Solution]
XML을 파싱해서 분석 후 CSV 형태로 저장하자.

[Review]
너무 간단하게 파싱이 되서 당황했습니다. 지금은 Java로 작성하는게 훨씬 편한거 같은데 python이 익숙해지면 훨씬 쉽게 작성할 수 있을 것 같습니다.
그리고 Linux에서는 python을 기본으로 제공해주니 별도의 설치도 필요 없습니다.(Python3는 별도로 설치해야함)

"""
# AndroidLintParser.py
# Author: coozplz@gmail.com
# Date: 2014. 08. 06
# Desc: Parse android stuidio's code analyze result(xml) to csv.
# usage: AndroidLintParser.py [-h] input output
    positional arguments:
      input       Android lint export path
      output      Output csv path
# Input:
<problems>
    <problem>
        <file>....</file>
        <line>9</line>
        <module>...</module>
        <package>...</package>
        <entry_point TYPE="field" FQNAME="..." />
        <problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">
                                    Declaration can have final modifier
        </problem_class>
        <hints />
        <description>Declaration can have final modifier</description>
  </problem>
</problems>
# Output:
=============================================================
    SEVERITY, MODULE, PACKAGE, FILE_NAME, LINE, DESC
=============================================================
"""
from xml.etree.ElementTree import parse
from xml.sax.saxutils import unescape
from os import listdir
from os import path
import argparse
import sys
'''
# 입력 파라미터를 설정한다.
'''
parser = argparse.ArgumentParser()
parser.add_argument("input", help="Android lint export path", type=str)
parser.add_argument("output", help="Output csv path", type=str)
args = parser.parse_args()

# 사용자가 input path의 마지막에 '/' 를 붙이지 않을 수도 있기 임의로 붙여준다.
# 두개를 붙여도 상관은 없다
XML_INPUT = args.input+"/"
CSV_OUTPUT = args.output


def parse_android_lint_to_csv(element):
    """
    AndroidLint 의 Problem Tag 를 분석하여 원하는 값을 추출한다.
    :param element: Problem Element
    :return: output String
    """
    filename = element.findtext("file")
    filename = filename[filename.rfind("/")+1:]
    entry_point = element.find("entry_point").get("FQNAME")
    entry_point = entry_point.split(' ')
    if len(entry_point) > 1:
        entry_point = entry_point[1]
    line = element.findtext("line")
    module = element.findtext("module")
    package = element.findtext("package")
    severity = element.find("problem_class").get("severity")
    desc = unescape(element.findtext("description"))
    if severity == "TYPO" or severity is "None":
        return
    return "{0},{1},{2},{3},{4},{5},{6}\n".format(severity,
                                                  module,
                                                  package,
                                                  filename,
                                                  entry_point,
                                                  line,
                                                  desc)


'''
#Main
'''
try:

    f = open(CSV_OUTPUT, "w")
except IOError:
    print('cannot open', CSV_OUTPUT)
else:
    try:
        # 제외 파일명 저장용 배열
        exceptFiles = []
        # 제외된 파일의 개수
        exceptFileCount = 0
        # 총 파일의 개수
        count = 0
        for iterFileName in listdir(XML_INPUT):
            count += 1
            if iterFileName.lower().find(".xml") < 0:
                '''
                # 파일명이 Xml 형식이 아닌것은 제외 목록에 추가하고 처리하지 않는다.
                '''
                exceptFileCount += 1
                exceptFiles.append(iterFileName)
                continue
            xmlDoc = parse(XML_INPUT + iterFileName)
            problems = xmlDoc.getroot()
            for parent in problems:
                data = parse_android_lint_to_csv(parent)
                if data:
                    # 데이터가 있는 경우에만 파일에 쓴다
                    f.write(data)
    except IOError as ioE:
        print("I/O error: {0}".format(ioE))
    except ValueError as ve:
        print("Could not convert data: {0}".format(ve))
    except:
        print("UnExpected error", sys.exc_info()[0])
        raise
finally:
    # 파일은 반드시 닫는다
    f.close()


# 결과를 출력
print("TOTAL={0}".format(count))
print("OUTPUT_FILE_NAME={0}".format(CSV_OUTPUT))
print("OUTPUT_FILE_SIZE=%0.2f KBytes" % (path.getsize(CSV_OUTPUT) / 1024))
if exceptFileCount > 0:
    print("EXCEPT={0}, EXCEPT_FILES={1}".format(exceptFileCount,
                                                exceptFiles))

[Python] 5-SmallestMultiple

알고리즘은 생각하지 않고 하루에 한 문제씩 풀면서 파이썬이란 언어와 친해지려고 합니다..
혹시 보시고 잘못된 부분이나 지적하실 부분이 있으면 댓글로 남겨주세요..

[문제 링크]
http://projecteuler.net/problem=5

[문제 요약]
1에서 20으로 모두 나눠지는 가장 작은 수를 구하시오..

[풀이 리뷰]
알고보면 간단한 문젠데 이해가 안되서 고민을 좀 했습니다.
프로젝트오일러에 나온 문제들은 대부분 1분내외로 풀어야 한다고 합니다.
제가 풀려고 했던 방법은 2의 배수중 20 보다 작은 가장 큰 수, 3의 배수중 20보다 작은 가장 큰수…. + 20이하의 모든 소수로 나눠지는 수를 계산하려고 했습니다.

나름 머리를 쓴다고 쓴게 반복을 최소화해서 속도를 높여보자였는데 간단한 방법이 있었습니다.

최소공배수(Least Common Multiple)를 이용하는 방법입니다.

lcm(lcm(lcm(lcm(1,2),3),4)…., 20)
위와 같은 방법으로 하면 연산을 많이 하지 않고도 답을 구할 수 있습니다.

[소스 코드]

"""
# 5_SmallestMultipl.py
# Date: 2014. 08. 04
# Author: coozplz@gmail.com
"""
import time

sTime = time.clock()
def gcd(a,b):
   """
   최대 공약수를 구하는 함수
   :param a: 숫자 입력값 1
   :param b: 숫자 입력값 2
   :return:최대 공약수
   """
   m = max(a,b)
   n = min(a,b)
   while n != 0:
       t = m % n
       (m,n)=(n,t)
   return abs(m)


"""
# 1. 숫자 1과 숫자 2의 최소공배수를 구한다.
# 2. 1의 결과와 3의 최소공배수를 구한다.
# ....
# 이전의 결과와 20의 최소공배수를 구한다.
"""

gcdValue = 1
for j in range(1, 21):
    # 최소 공배수를 구한다
    gcdValue = int(gcdValue * j / gcd(gcdValue, j))
    i = j
print(gcdValue, "최소공배수 이용=", (time.clock() - sTime) * 100)

"""
# 단순 무식하게 반복문으로 1씩 증가하면서 모두 해본다.
"""
sTime = time.clock()
num = 1
while True:
    found = True
    for i in range(1, 21):
        if num % i != 0:
            found = False
            break
    if found:
        break
    num += 1
print(num, "반복문만 사용=", (time.clock() - sTime) * 100)

[풀이 시간]
최소공배수 이용= 0.0030000000000000512
반복문만 사용= 14033.343700000001