Symfoware

Symfowareについての考察blog

Python3 別途ライブラリをインストールせず、ImageMagickを使用するpymraw

Python3から直接ImageMagickのコマンドを実行する方法を調べてみました。
Python3 subprocess + ImageMagick identifyで画像のexif情報を取得する
Python3 subprocess + ImageMagick convertで画像のリサイズやトリミングを実行
Python3 subprocess + ImageMagick convert プログラム中のバイナリデータを変換

材料が揃ったので、ソースをまとめてクラス化しました。
https://bitbucket.org/symfo/pymraw

ImageMagickがインストールされていることと、Python 3.5以上であることが条件です。
機能は少ないですが、ウリは...

・1ファイル置くだけで使える
・テンポラリファイルを作成しない

AWS LambdaのPythonでサムネールを作りたかったので、こういうのが欲しかった。



インストール



https://bitbucket.org/symfo/pymraw
ここからpymraw.pyをダウンロードして、ソースファイルと同じ階層においておきます。



サンプル



画像のサイズやexif情報を取得し、リサイズを行うサンプル。


  1. # -*- coding:utf-8 -*-
  2. import os
  3. import pymraw
  4. base_dir = os.path.abspath(os.path.dirname(__file__))
  5. with open(os.path.join(base_dir, 'cc2.jpg'), 'rb') as f:
  6.     img = f.read()
  7. # バイナリデータを指定してインスタンス生成
  8. m = pymraw.Imagick(img)
  9. # 各種情報を表示
  10. print(m.width())
  11. print(m.height())
  12. print(m.exif('DateTimeOriginal'))
  13. # リサイズ実行 stripは自動で行われる
  14. r = m.resize(300, 300)
  15. # バイナリデータを取得してファイルを保存
  16. with open('test.jpg', 'wb') as o:
  17.     o.write(r.get_blob())




resize(width, height, is_strip, quality)
width:幅
height:高さ
is_strip:stripするか(deafult:True)
quality:変換後のクオリティー(1-100 default:100)



という感じです。





回転と切り抜き



回転や切り抜きはこんな感じになります。


  1. # -*- coding:utf-8 -*-
  2. import os
  3. import pymraw
  4. base_dir = os.path.abspath(os.path.dirname(__file__))
  5. with open(os.path.join(base_dir, 'cc2.jpg'), 'rb') as f:
  6.     img = f.read()
  7. # バイナリデータを指定してインスタンス生成
  8. m = pymraw.Imagick(img)
  9. # 回転
  10. r = m.rotate(90)
  11. # バイナリデータを取得してファイルを保存
  12. with open('test-1.jpg', 'wb') as o:
  13.     o.write(r.get_blob())
  14. # 切り抜き
  15. c = m.crop(500, 500, 10, 20)
  16. with open('test-2.jpg', 'wb') as o:
  17.     o.write(c.get_blob())



回転

rotate( degrees )
degrees : 画像を回転させる角度(時計回り)




切り抜き


crop ( width , height , x , y )
width:抽出する幅
height:抽出する高さ
x:抽出する領域の左上の X 座標
y:抽出する領域の左上の Y 座標






透かし画像挿入



compositeで透かし画像を入れます。
スタイルはtile固定です。


  1. # -*- coding:utf-8 -*-
  2. import os
  3. import pymraw
  4. base_dir = os.path.abspath(os.path.dirname(__file__))
  5. with open(os.path.join(base_dir, 'cc2.jpg'), 'rb') as f:
  6.     img = f.read()
  7. # バイナリデータを指定してインスタンス生成
  8. m = pymraw.Imagick(img)
  9. # 透かし画像を入れる
  10. r = m.composite(os.path.join(base_dir, 'lace_round_5.png'))
  11. # バイナリデータを取得してファイルを保存
  12. with open('test-3.jpg', 'wb') as o:
  13.     o.write(r.get_blob())





composite(water_mark_fike)
water_mark_fike:透かし画像のパス




AWS Lambdaで使えるか試してみます。

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/12(土) 14:00:25|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Python3 subprocess + ImageMagick convert プログラム中のバイナリデータを変換

Python3 + subprocessからconvertコマンドを呼び出して画像を変換してみました。
Python3 subprocess + ImageMagick identifyで画像のexif情報を取得する
Python3 subprocess + ImageMagick convertで画像のリサイズやトリミングを実行

変換元や変換先はファイルに出力されます。
例えば、プログラム中でネットワーク上の画像データを取得。
そのデータをリサイズしたい時、ファイルに出力せずバイナリデータを直接変換したい。


標準出力と標準入力



imageMagickのconvert で標準入出力 stdout/stdin を使う


$ command | convert - -option arg - | command



ハイフンを使用して、標準出入力を制御してやれば良さそうです。

試しに、こんなコマンドを実行。


$ convert cc2.jpg -resize 100x100 cc2-resize.jpg




この形でも同様の結果が出力されました。


$ cat cc2.jpg | convert - -resize 100x100 - > dst.jpg








stdinとstdout



https://docs.python.jp/3.5/library/subprocess.html

subprocess.runするときの、stdinとstdoutに画像データを指定すれば良いのではないか。

https://docs.python.jp/3.5/library/subprocess.html
こちらを参考にプログラムを書いてみました。


  1. # -*- coding:utf-8 -*-
  2. import io
  3. import subprocess
  4. # 画像のバイナリデータ取得
  5. with open('cc2.jpg', 'rb') as f:
  6.     img = f.read()
  7. # 変換コマンドを用意
  8. proc = subprocess.Popen(['convert - -resize 200x200 -'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
  9. # 標準入力にバイナリデータ書き込み
  10. # 結果を受け取る
  11. outs, errs = proc.communicate(img)
  12. # 確認のため、結果をファイルに出力
  13. with open('cc2-resize.jpg', 'wb') as o:
  14.     o.write(outs)




これでうまく変換できました。
ファイルに出力せず画像の変換が行えそうです。

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/11(金) 18:23:39|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Python3 subprocess + ImageMagick convertで画像のリサイズやトリミングを実行

Python3 + subprocessでImageMagickのコマンドを直接実行し、
画像の情報を取得してみました。
Python3 subprocess + ImageMagick identifyで画像のexif情報を取得する

今回は、画像のリサイズや回転、トリミングを行ってみます。


resize



リサイズしたい時に実行するコマンドは


$ convert [元画像] -resize [幅px]x[高さpx] [出力画像]
$ convert image.jpg -resize 300x200 image2.jpg




プログラムで記載するとこうなりました。


  1. # -*- coding:utf-8 -*-
  2. import subprocess
  3. r = subprocess.run(['convert', 'photo3.jpg', '-resize', '300x300', 'photo3-resize.jpg'], stdout=subprocess.PIPE)
  4. print('return code:%d' % r.returncode)




縦横ともに300pxを指定しています。
300x300の正方形の画像が出力されるわけではなく、画像の長い部分に合わせられます。
縦横比が崩れる事はありません。


リサイズ時、exif情報も削除したい場合はthumbnailを使用すれば良さそうです。


  1. r = subprocess.run(['convert', 'photo3.jpg', '-thumbnail', '300x300', 'photo3-resize.jpg'], stdout=subprocess.PIPE)
  2. print('return code:%d' % r.returncode)




もうひとつ、stripコマンドと組み合わせるてもあります。


  1. r = subprocess.run(['convert', 'photo3.jpg', '-thumbnail', '300x300', '-strip', 'photo3-resize.jpg'], stdout=subprocess.PIPE)
  2. print('return code:%d' % r.returncode)




組み合わせを試してみたところ、テスト画像ではresize stripが一番サイズが小さくなりました。


resize 89.7kb
thumbnail 57.3kb
resize strip 57.0kb






rotate



画像の回転はrotateを指定します。


$ convert [元画像] -rotate [角度(右回り)] [出力画像]
$ convert image.jpg -rotate 90 image2.jpg




プログラムで記載するとこうなりました。


  1. # -*- coding:utf-8 -*-
  2. import subprocess
  3. r = subprocess.run(['convert', 'cc2.jpg', '-rotate', '90', 'cc2-rotate.jpg'], stdout=subprocess.PIPE)
  4. print('return code:%d' % r.returncode)








crop



画像の任意の箇所を切り出したい場合はcropを使用します。


$ convert [元画像] -crop [切り抜き位置] [出力画像]
$ convert image.jpg -crop 100x100+0+0 image2.jpg




切り抜き位置の指定ですが、
[切り抜く矩形の幅]x[切り抜く矩形の高さ]+[切り抜き開始点X]+[切り抜き開始点Y]


プログラムで記載するとこうなりました。


  1. # -*- coding:utf-8 -*-
  2. import subprocess
  3. r = subprocess.run(['convert', 'cc2.jpg', '-crop', '600x300+100+50', 'cc2-crop.jpg'], stdout=subprocess.PIPE)
  4. print('return code:%d' % r.returncode)








composite



画像に透かしを入れるには、compositeを使用します。


$ convert [元画像] [透かし画像] -composite [出力画像]
$ convert cc2.jpg lace_round_5.png -composite cc2-composite.jpg




元画像はこちら。
https://blogs.yahoo.co.jp/kirinogensou/1640645.html

透かし画像用の透過pngはこちらかを使用させていただきました。
http://www.g-floor.org/archives/2087

元画像
769_01.jpg

透かしを入れた画像
769_02.jpg


透かしの位置を指定する場合は-gravityで指定します。
NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEastが指定可能です。


$ convert cc2.jpg lace_round_5.png -gravity SouthEast -composite cc2-composite.jpg



769_03.jpg


透かし画像を繰り返しタイル上に敷き詰めたい場合は、画像合成専用のコマンド
compositeを使用したほうが簡単です。

composite
http://www.imagemagick.org/Usage/annotating/#wmark_image



$ composite -tile lace_round_5.png cc2.jpg cc2-composite.jpg



769_04.jpg


プログラムではこうなりました。


  1. # -*- coding:utf-8 -*-
  2. import subprocess
  3. r = subprocess.run(['composite', '-tile', 'lace_round_5.png', 'cc2.jpg', 'cc2-crop.jpg'], stdout=subprocess.PIPE)
  4. print('return code:%d' % r.returncode)


テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/11(金) 16:36:36|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Python3 subprocess + ImageMagick identifyで画像のexif情報を取得する

Pythonで画像処理を行う場合、PillowやImageMagickのラッパーWandを使うことになるかと思います。
ImageMagickがインストールされた環境で、別のライブラリに依存せず
なんとかならないか考えてみました。

ImageMagickがインストール済のUbuntu 16.04 + Python 3.5で試しています。


subprocess



Python3で別のプロセスを実行するには、subprocessを使うのがよさそうです。
17.5. subprocess - サブプロセス管理

Python2の頃に比べて随分便利になりました。

subprocess.runで任意のコマンドを実行。
戻り値であるsubprocess.CompletedProcessにリターンコードが保存されています。
また、run実行時にstdout=subprocess.PIPEを指定しておけば、
戻り値のstdoutプロパティに、実行結果がバイナリで保存されています。
※universal_newlines=Trueを指定すれば文字列となる。


サンプルを実行して、ls -lの結果を表示してみます。


  1. # -*- coding:utf-8 -*-
  2. import subprocess
  3. r = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
  4. print(r.returncode)
  5. output = r.stdout.decode("utf-8")
  6. for line in output.split("\n"):
  7.     print(line)
  8. $ python3 sample.py
  9. return code:0
  10. 合計 21876
  11. -rw-r----- 1 baranche baranche 221748 Nov 6 2016 cc2.jpg
  12. -rw-r----- 1 baranche baranche 107982 Nov 6 2016 img_0.jpg
  13. -rw-r----- 1 baranche baranche    89307 Nov 6 2016 img_1.jpg
  14. -rw-rw-r-- 1 baranche baranche 20123547 Apr 17 21:31 photo3.jpg
  15. -rw-rw-r-- 1 baranche baranche     230 Aug 11 14:17 sample.py
  16. -rw-rw-r-- 1 baranche baranche 1835272 Aug 11 13:57 soku_34681.jpg



ちゃんとlsの結果が取得できたようです。






identify



exif情報の前に、画像のサイズを取得してみます。

ImageMagickは画像の変換を行う「convert」コマンドが有名ですが、
画像の情報を取得する「identify」コマンドもあるようです。
今回初めて知りました。
identify

コマンドの実行結果は以下のようになります。


$ identify cc2.jpg
cc2.jpg JPEG 1024x768 1024x768+0+0 8-bit sRGB 222KB 0.020u 0:00.030




この文字列をそのまま解析しても良いのですが、identifyにはformatオプションがあります。
https://www.imagemagick.org/script/command-line-options.php
https://www.imagemagick.org/script/escape.php

%wで幅、%hで高さのピクセル数の表示となります。


$ identify -format '%w %h' cc2.jpg
1024 768




これを利用して、画像サイズを取得するプログラムを作成してみます。


  1. # -*- coding:utf-8 -*-
  2. import subprocess
  3. r = subprocess.run(['identify', '-format', '%w %h', 'cc2.jpg'], stdout=subprocess.PIPE)
  4. print('return code:%d' % r.returncode)
  5. output = r.stdout.decode("utf-8")
  6. width, height = output.split()
  7. print('width:' + width)
  8. print('height:' + height)





狙いい通りの結果が得られました。


$ python3 sample.py
return code:0
width:1024
height:768







exif



exifの情報もidentifyコマンドに「-verbose」オプションをつければ取得できます。


$ identify -verbose photo3.jpg
Image: photo3.jpg
Format: JPEG (Joint Photographic Experts Group JFIF format)
Mime type: image/jpeg
Class: DirectClass
Geometry: 8688x5792+0+0
Resolution: 72x72
Print size: 120.667x80.4444
Units: PixelsPerInch
Type: TrueColor
Endianess: Undefined
Colorspace: sRGB
Depth: 8-bit
Channel depth:
    red: 8-bit
    green: 8-bit
    blue: 8-bit
Channel statistics:
    Pixels: 50320896
    Red:
     min: 0 (0)
     max: 255 (1)
     mean: 46.3726 (0.181853)
     standard deviation: 52.4237 (0.205583)
     kurtosis: 2.42003
     skewness: 1.74154
    Green:
     min: 0 (0)
     max: 255 (1)
     mean: 41.9346 (0.164449)
     standard deviation: 47.6014 (0.186672)
     kurtosis: 2.56682
     skewness: 1.77416
    Blue:
     min: 0 (0)
     max: 255 (1)
     mean: 30.7038 (0.120407)
     standard deviation: 43.5288 (0.170701)
     kurtosis: 4.47736
     skewness: 2.16726
Image statistics:
    Overall:
     min: 0 (0)
     max: 255 (1)
     mean: 39.6703 (0.15557)
     standard deviation: 47.9892 (0.188193)
     kurtosis: 3.29406
     skewness: 1.93119
Rendering intent: Perceptual
Gamma: 0.454545
Chromaticity:
    red primary: (0.64,0.33)
    green primary: (0.3,0.6)
    blue primary: (0.15,0.06)
    white point: (0.3127,0.329)
Background color: white
Border color: srgb(223,223,223)
Matte color: grey74
Transparent color: black
Interlace: None
Intensity: Undefined
Compose: Over
Page geometry: 8688x5792+0+0
Dispose: Undefined
Iterations: 0
Compression: JPEG
Quality: 98
Orientation: TopLeft
Properties:
    date:create: 2017-08-11T13:46:22+09:00
    date:modify: 2017-04-17T21:31:29+09:00
    exif:ApertureValue: 458752/65536
    exif:Artist:
    exif:ColorSpace: 1
    exif:ComponentsConfiguration: 1, 2, 3, 0
    exif:Copyright:
    exif:CustomRendered: 0
    exif:DateTime: 2014:12:12 16:52:16
    exif:DateTimeDigitized: 2014:12:12 16:52:16
    exif:DateTimeOriginal: 2014:12:12 16:52:16
    jpeg:colorspace: 2
    jpeg:sampling-factor: 2x1,1x1,1x1
    signature: 2cfa45ef3189f6b05eaac4e529fb643614be8e76d6f76afffbb278045c4e521e
    unknown: 2
    xmp:Rating: 3
Profiles:
    Profile-exif: 30078 bytes
    Profile-xmp: 2529 bytes
Artifacts:
    filename: photo3.jpg
    verbose: true
Tainted: False
Filesize: 20.12MB
Number pixels: 50.32M
Pixels per second: 86.76MB
User time: 0.570u
Elapsed time: 0:01.580
Version: ImageMagick 6.8.9-9 Q16 x86_64 2017-07-31 http://www.imagemagick.org





出力された文字列を解析しても良いのですが、欲しいexif情報だけ。
これもformatを指定することで、exif情報のみ取得できました。
https://www.imagemagick.org/script/escape.php

フォーマットに%[exif:*]を指定すればOKです。


$ identify -format '%[exif:*]' photo3.jpg
exif:ApertureValue=458752/65536
exif:Artist=
exif:ColorSpace=1
exif:ComponentsConfiguration=1, 2, 3, 0
exif:Copyright=
exif:CustomRendered=0
exif:DateTime=2014:12:12 16:52:16
exif:DateTimeDigitized=2014:12:12 16:52:16
exif:DateTimeOriginal=2014:12:12 16:52:16
exif:ExifImageLength=5792
exif:ExifImageWidth=8688
exif:ExifOffset=360
exif:ExifVersion=48, 50, 51, 48
exif:ExposureBiasValue=0/1
exif:ExposureMode=1
exif:ExposureProgram=1
exif:ExposureTime=1/15
exif:Flash=16
exif:FlashPixVersion=48, 49, 48, 48
exif:FNumber=11/1
exif:FocalLength=35/1
exif:FocalPlaneResolutionUnit=2
exif:FocalPlaneXResolution=8688000/1450
exif:FocalPlaneYResolution=5792000/965
exif:GPSInfo=10348
exif:GPSVersionID=2, 3, 0, 0
exif:InteroperabilityOffset=10120
exif:ISOSpeedRatings=400
exif:Make=Canon
exif:MeteringMode=5
exif:Model=Canon EOS 5DS R
exif:Orientation=1
exif:ResolutionUnit=2
exif:SceneCaptureType=0
exif:ShutterSpeedValue=262144/65536
exif:SubSecTime=00
exif:SubSecTimeDigitized=00
exif:SubSecTimeOriginal=00
exif:thumbnail:Compression=6
exif:thumbnail:InteroperabilityIndex=R98
exif:thumbnail:InteroperabilityVersion=48, 49, 48, 48
exif:thumbnail:JPEGInterchangeFormat=12236
exif:thumbnail:JPEGInterchangeFormatLength=17836
exif:thumbnail:ResolutionUnit=2
exif:thumbnail:XResolution=72/1
exif:thumbnail:YResolution=72/1
exif:WhiteBalance=1
exif:XResolution=72/1
exif:YCbCrPositioning=2
exif:YResolution=72/1





単一の項目のみ取得したい場合は、*ではなく項目名を指定してやります。


$ identify -format '%[exif:DateTimeOriginal]' photo3.jpg
2014:12:12 16:52:16




これ、どこかで見たことあるフォーマットだなと思ったら
PHPでImageMagickを使用する際のgetImagePropertiesの指定と同じですね。
http://php.net/manual/ja/imagick.getimageproperties.php



formatの指定によって、高さや幅、exif情報を一気に取得することもできました。


$ identify -format '%w\n%h\n%[exif:*]' photo3.jpg
8688
5792
exif:ApertureValue=458752/65536
exif:Artist=
exif:ColorSpace=1
(略)





これらを踏まえて、プログラムを書いてみます。


  1. # -*- coding:utf-8 -*-
  2. import subprocess
  3. r = subprocess.run(['identify', '-format', 'width:%w\nheight:%h\n%[exif:*]', 'photo3.jpg'], stdout=subprocess.PIPE)
  4. print('return code:%d' % r.returncode)
  5. output = r.stdout.decode("utf-8")
  6. width = 0
  7. height = 0
  8. exif = {}
  9. for line in output.split('\n'):
  10.     info = line.strip().split(':', maxsplit=1)
  11.     if len(info) < 2:
  12.         continue
  13.     
  14.     if info[0] == 'width':
  15.         width = info[1]
  16.         continue
  17.     if info[0] == 'height':
  18.         height = info[1]
  19.         continue
  20.     # exif
  21.     exif_pair = info[1].split('=', maxsplit=1)
  22.     exif[exif_pair[0]] = exif_pair[1]
  23. print('width = ' + width)
  24. print('height = ' + height)
  25. print('exif:DateTimeOriginal = ' + exif['DateTimeOriginal'])




ちゃんと画像の情報が取得できました。


$ python3 sample.py
return code:0
width = 8688
height = 5792
exif:DateTimeOriginal = 2014:12:12 16:52:16


テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/11(金) 15:10:19|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Debian easy_installで「SSL: CERTIFICATE_VERIFY_FAILED」エラー

ここでインストールしたDebian 9
Debian 9 (Stretch)を最小構成でインストールしssh接続を許可する

Pythonの環境をインストールしました。


# apt-get install python
# apt-get install python-setuptools




easy_installでcythonをインストールしようとしたら、
「SSL: CERTIFICATE_VERIFY_FAILED」
というエラーが発生。


# easy_install cython
Searching for cython
Reading https://pypi.python.org/simple/cython/
Download error on https://pypi.python.org/simple/cython/: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661) -- Some packages may not be found!
Couldn't find index page for 'cython' (maybe misspelled?)
Scanning index of all packages (this may take a while)
Reading https://pypi.python.org/simple/
Download error on https://pypi.python.org/simple/: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661) -- Some packages may not be found!
No local packages or working download links found for cython
error: Could not find suitable distribution for Requirement.parse('cython')




FreeBSDの時にも同様のエラーが発生したので、これを参考に
FreeBSD 10.1で、easy_installのSSLエラー(CERTIFICATE_VERIFY_FAILED)
ca-certificates package


「ca-certificates」をインストールしたら、エラーが解消されました。


# apt-get install ca-certificates



テーマ:サーバ - ジャンル:コンピュータ

  1. 2017/06/19(月) 23:04:53|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ