3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

lambda で heic 形式の画像を扱うためにやったこと

Posted at

背景

iphone のカメラで保存された画像はデフォで heic という形式になっており、web ブラウザは現状解釈してくれない。そのため、他のjpgなどの画像と同じように扱うためにはサーバ側で加工する必要がある。1 今回は、サーバ側でどうにかする手段をやってみたので残しとこうと思う。

内容

結論

lambda に imagemagick を仕込んだ lambda layer を連携させて、imagemagick を叩けばok

実装方法

lambda の runtime は node v12
lambda の管理には serverless framework
lambda layer はこのリポジトリのものを使う。ただ、 heic を扱うためのライブラリが入っていないので同リポジトリに上がっていたPRからビルド手段を持ってきました。imagemagick のビルドフラグで heic を追加したり、必要なモジュールである、libheif, libde265 をビルドできるようにしている。

LIBPNG_VERSION ?= 1.6.37
LIBJPG_VERSION ?= 9c
OPENJP2_VERSION ?= 2.3.1
LIBTIFF_VERSION ?= 4.0.9
BZIP2_VERSION ?= 1.0.6
LIBWEBP_VERSION ?= 0.6.1
IMAGEMAGICK_VERSION ?= 7.0.8-68
LIBHEIF_VERSION ?= 1.6.0
LIBDE265_VERSION ?= 1.0.3

TARGET_DIR ?= /opt/
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
CACHE_DIR=$(PROJECT_ROOT)build/cache

.ONESHELL:

CONFIGURE = PKG_CONFIG_PATH=$(CACHE_DIR)/lib/pkgconfig \
	./configure \
		CPPFLAGS=-I$(CACHE_DIR)/include \
		LDFLAGS=-L$(CACHE_DIR)/lib \
		--disable-dependency-tracking \
		--disable-shared \
		--enable-static \
		--prefix=$(CACHE_DIR)

## libde265
LIBDE265_SOURCE=libde265-$(LIBDE265_VERSION).tar.gz

$(LIBDE265_SOURCE):
	curl -LO https://github.com/strukturag/libde265/releases/download/v$(LIBDE265_VERSION)/$(LIBDE265_SOURCE)

$(CACHE_DIR)/lib/libde265.a: $(LIBDE265_SOURCE)
	tar xf $<
	cd libde265*
	./autogen.sh
	$(CONFIGURE)
	make
	make install

## libheic
LIBHEIF_SOURCE=libheif-$(LIBHEIF_VERSION).tar.gz

$(LIBHEIF_SOURCE):
	curl -LO https://github.com/strukturag/libheif/releases/download/v$(LIBHEIF_VERSION)/$(LIBHEIF_SOURCE)

$(CACHE_DIR)/lib/libheif.a: $(LIBHEIF_SOURCE)
	tar xf $<
	cd libheif*
	./autogen.sh
	$(CONFIGURE)
	make
	make install


## libjpg

LIBJPG_SOURCE=jpegsrc.v$(LIBJPG_VERSION).tar.gz

$(LIBJPG_SOURCE):
	curl -LO http://ijg.org/files/$(LIBJPG_SOURCE)

$(CACHE_DIR)/lib/libjpeg.a: $(LIBJPG_SOURCE)
	tar xf $<
	cd jpeg*
	$(CONFIGURE)	 
	make
	make install


## libpng

LIBPNG_SOURCE=libpng-$(LIBPNG_VERSION).tar.xz

$(LIBPNG_SOURCE):
	curl -LO http://prdownloads.sourceforge.net/libpng/$(LIBPNG_SOURCE)

$(CACHE_DIR)/lib/libpng.a: $(LIBPNG_SOURCE)
	tar xf $<
	cd libpng*
	$(CONFIGURE)	 
	make
	make install

# libbz2

BZIP2_SOURCE=bzip2-$(BZIP2_VERSION).tar.gz

$(BZIP2_SOURCE):
	curl -LO http://prdownloads.sourceforge.net/bzip2/bzip2-$(BZIP2_VERSION).tar.gz

$(CACHE_DIR)/lib/libbz2.a: $(BZIP2_SOURCE)
	tar xf $<
	cd bzip2-*
	make libbz2.a
	make install PREFIX=$(CACHE_DIR)

# libtiff

LIBTIFF_SOURCE=tiff-$(LIBTIFF_VERSION).tar.gz

$(LIBTIFF_SOURCE):
	curl -LO http://download.osgeo.org/libtiff/$(LIBTIFF_SOURCE)

$(CACHE_DIR)/lib/libtiff.a: $(LIBTIFF_SOURCE) $(CACHE_DIR)/lib/libjpeg.a
	tar xf $<
	cd tiff-*
	$(CONFIGURE)	 
	make
	make install

# libwebp

LIBWEBP_SOURCE=libwebp-$(LIBWEBP_VERSION).tar.gz

$(LIBWEBP_SOURCE):
	curl -L https://github.com/webmproject/libwebp/archive/v$(LIBWEBP_VERSION).tar.gz -o $(LIBWEBP_SOURCE)
	
$(CACHE_DIR)/lib/libwebp.a: $(LIBWEBP_SOURCE)
	tar xf $<
	cd libwebp-*
	sh autogen.sh
	$(CONFIGURE)	 
	make
	make install

## libopenjp2

OPENJP2_SOURCE=openjp2-$(OPENJP2_VERSION).tar.gz

$(OPENJP2_SOURCE):
	curl -L https://github.com/uclouvain/openjpeg/archive/v$(OPENJP2_VERSION).tar.gz -o $(OPENJP2_SOURCE)


$(CACHE_DIR)/lib/libopenjp2.a: $(OPENJP2_SOURCE) $(CACHE_DIR)/lib/libpng.a $(CACHE_DIR)/lib/libtiff.a
	tar xf $<
	cd openjpeg-*
	mkdir -p build
	cd build 
	PKG_CONFIG_PATH=$(CACHE_DIR)/lib/pkgconfig cmake .. \
		-DCMAKE_BUILD_TYPE=Release \
		-DCMAKE_INSTALL_PREFIX=$(CACHE_DIR) \
		-DBUILD_SHARED_LIBS:bool=off \
		-DBUILD_CODEC:bool=off
	make clean
	make install


## ImageMagick

IMAGE_MAGICK_SOURCE=ImageMagick-$(IMAGEMAGICK_VERSION).tar.gz

$(IMAGE_MAGICK_SOURCE):
	curl -L https://github.com/ImageMagick/ImageMagick/archive/$(IMAGEMAGICK_VERSION).tar.gz -o $(IMAGE_MAGICK_SOURCE)


LIBS:=$(CACHE_DIR)/lib/libjpeg.a \
	$(CACHE_DIR)/lib/libpng.a \
	$(CACHE_DIR)/lib/libopenjp2.a \
	$(CACHE_DIR)/lib/libtiff.a \
	$(CACHE_DIR)/lib/libbz2.a \
	$(CACHE_DIR)/lib/libwebp.a \
	$(CACHE_DIR)/lib/libde265.a \
	$(CACHE_DIR)/lib/libheif.a \
	$(CACHE_DIR)/lib/libwebp.a

$(TARGET_DIR)/bin/identify: $(IMAGE_MAGICK_SOURCE) $(LIBS)
	tar xf $<
	cd ImageMa*
	PKG_CONFIG_PATH=$(CACHE_DIR)/lib/pkgconfig \
		./configure \
		CPPFLAGS=-I$(CACHE_DIR)/include \
		LDFLAGS="-L$(CACHE_DIR)/lib -lstdc++" \
		--disable-dependency-tracking \
		--disable-shared \
		--enable-static \
		--with-heic=yes \
		--prefix=$(TARGET_DIR) \
		--enable-delegate-build \
		--without-modules \
		--disable-docs \
		--without-magick-plus-plus \
		--without-perl \
		--without-x \
		--disable-openmp
	make clean
	make all
	make install

libs: $(LIBS)

all: $(TARGET_DIR)/bin/identify

リポジトリ内にあるMakefileをみてもらえればわかるが、これらのビルド処理はlambda 実行マシンに近いdocker image 内で実行されるのでlinux 環境は必要ない。
make all ですべてが解決する。lambda layer を登録したらその arn を serverless.yml に追記して変更を反映する。

functions:
  preview-img:
    runtime: nodejs12.x
    handler: bin/index.preview
    memorySize: 1536
    layers:
      - arn:aws:lambda:ap-northeast-1:xxxxxxxxx:layer:image-magick:4
    events:
      - http:
          path: preview
          method: post
          cors:
            origin: "*"
            maxAge: 86400

api gateway も一応画像の content-type を絞っておく

provider:
  #
  # 
  # 
  apiGateway:
    minimumCompressionSize: 0 # gzip で返却できるように
    binaryMediaTypes:
      - image/png
      - image/jpeg
      - image/jpg
      - image/heic

ここまで設定が終わればあとは処理を書くだけ。node は不慣れなので書き方が雑ですがこんな風にやった。
lambda の中でファイル書き込みを行いときは /tmp に書くというのがお作法のようでそれに習っている。

const { execSync } = require('child_process');
const fs = require('fs');

module.exports.preview = (event, context, callback) => {
    buf = new Buffer(event.body, 'base64')

    heicName = '/tmp/' + context.awsRequestId + '.heic'
    jpgName = '/tmp/' + context.awsRequestId + '.jpg'
    console.time('writeFileSync');
    fs.writeFileSync(heicName, buf, function (err) {
        console.log(`err: ${err}`);
        if (err) throw err;
    });
    console.timeEnd('writeFileSync');

    cmd = '/opt/bin/convert' + ' ' + heicName + ' ' + jpgName
    console.log(`cmd: ${cmd}`);

    console.time('convert');
    try {
        stdout = execSync(cmd);
        console.log(`cmd stdout: ${stdout.toString()}`)
    } catch (err) {
        console.log(`cmd err: ${err}`)
    }
    console.timeEnd('convert');

    stdout = execSync('ls -la /tmp')
    console.log(`ls -la: ${stdout.toString()}`)

    // return response
};

その他学んだこと

ほんとは go でやりたかったけど、lambda で使われるマシンが Amazon Linux2 ではないのでダメだった。node10以降, python3 系は対応している。

他にも便利そうなlambda layer は awesome にまとまってる。ffmpeg とかもある。

memorySize: 1536 を振っていても、2~3 MB くらいの画像だと1.5秒かかるのでもうちょい早くならんのかなぁというところ

免責
記事内容の運用により派生した損害を含むあらゆる結果については一切の責任を持ちません。

  1. もしかしたら、この ライブラリ を使えばクライアントサイドでどうにかなるので不要かもしれない

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?