指定の画像のドットがすべて白色かをチェックする(=画像の空白判定)AppleScriptです。
画像の空白判定処理は自分的にはひじょうに重要な処理であり、画像をグレースケール化しておいて、
(方法1)Photoshopを用いて明度ヒストグラムを取得し、明度=255のデータだけが存在することを確認
(方法2)GPUImage.frameworkを用いて明度ヒストグラムを取得し、明度=255のデータだけが存在することを確認
といった方法で確認を行なっていました。
PDFの余白ページ判定処理や、画像同士の差分確認など、Photoshopを使わずに済めば利用範囲も広がるため(Mac App Storeに出せるため)、AppleScript+Frameworkぐらいで高速処理できることにはものすごく価値があります。
そんな中、GPUImageは急速に2度の方向転換を行い、AppleScriptからは付き合いにくいフレームワークに変化しました。
全面的にSwiftで書き換えたGPUImage2、さらにmacOS 10.14で行われた「OpenGL/OpenCLの非推奨化」という方針転換(わかっていたことですが)を受け、Metalを活用するように書き換えられたGPUImage3へと姿を変えました。オリジナルから見るとほぼ別物です。
GPUImage 3はまだまだ機能不足なうえにAppleScriptから呼べない状態。ヒストグラムの計算フィルタも搭載されていません。Objective-Cで書かれ、中国のスマホ開発者が写真加工するのに活用しまくった、人民に愛されまくったGPUImageの姿はもう見られないのでしょうか。
GPUImageを用いた他のフィルタ処理はCIFilterで代替できるのであまり問題にはなりませんが、この空白画像検出処理だけはなんとしても代替手段を見つける必要に迫られました。応用例が多すぎるからです。
そこで思いついたのが、「チェック対象の画像と同サイズの白い画像を作って、データ内容が同じかどうか調べる」というシンプルな方法(最初から思いついてほしい>自分)。
これならCPUパワーもそれほど必要とせず、GPUの力を絞り出す必要もありません(あたりまえ)。
さっそく書いてみたものの、今度はどうも「白い色」の値が合わず、頭をひねりまくりました。
▲1×1ドット画像を新規作成して白く塗りつぶして比較。Photoshopで作成してファイルから読み込んだ画像とDataが同じにならない
カラープロファイルが合わないために「白い色」を指定してもイコールにならないようだったので、オリジナル画像をコピーしてそれ自体を白く塗りつぶして空白検出の比較対象としてみました。これで空白検出が無事できるようになりました。しかも、GPUImage.frameworkを使っていたバージョンよりもあからさまに高速、、、、
処理速度をPhotoshop版、GPUImage版のAppleScriptと比較してみたところ、1980×1200ピクセルぐらいの画像だとGPUImage版の倍ぐらい高速、8K(7680×4320)ぐらいになるとPhotoshopに負けるといったところです。
▲同一環境にて、Photoshop CC 2018、GPUImage、本Scriptで各種サイズの画像の空白検出を実行(単位:秒)
処理内容がシンプルなだけに小さい画像の処理は得意で、大きな画像は不向きといえるかもしれません。テスト機は例によってCore i7 2.66GHzメモリ8GBのマシンであり、より搭載メモリ量の多いマシンで実行すると挙動が変わってくるかもしれません。
補足までに、GPUImageの明度ヒストグラム検出は、結果を数値の配列ではなく、1×256ドットの「画像」として返してくる変態仕様なので、結果を判定するために1×256ピクセルの画像をループでチェックする必要があります。この仕様が余計なオーバーヘッドを生んでいる(つまり、GPUで処理しているから爆速、という世間の期待値を大幅に下回る処理内容になっている)可能性は否定できません。
ただ、PhosothopなしでPhotoshopと同様のヒストグラム処理が行えるという「手軽さ」がいいと思ってGPUImageを使い出したので、速度をベンチマークしてみると「こんなもんだろ」という印象です。
AppleScript名:画像の空白判定 v3.scpt |
— – Created by: Takaaki Naganoya – Created on: 2019/03/07 — – Copyright © 2019 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use framework "AppKit" use scripting additions property NSData : a reference to current application’s NSData property NSDate : a reference to current application’s NSDate property |NSURL| : a reference to current application’s |NSURL| property NSColor : a reference to current application’s NSColor property NSImage : a reference to current application’s NSImage property NSBezierPath : a reference to current application’s NSBezierPath property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep set aPOSIXpath to POSIX path of (choose file of type {"public.image"}) set a1Dat to NSDate’s timeIntervalSinceReferenceDate() set iRes to checkImageIsWhite(aPOSIXpath) of me set b1Dat to NSDate’s timeIntervalSinceReferenceDate() set c1Dat to b1Dat – a1Dat return {iRes, c1Dat} –Compare Original Data and on checkImageIsWhite(aPOSIXpath) set aURL to |NSURL|’s fileURLWithPath:(aPOSIXpath) set anNSImage to NSImage’s alloc()’s initWithContentsOfURL:(aURL) copy anNSImage to bNSImage set fillColor to makeNSColorFromRGBAval(65535, 65535, 65535, 65535, 65535) of me –set fillColor to NSColor’s whiteColor() set blankImage to drawImageWithFilledColor(bNSImage, fillColor) of me set imgA to anNSImage’s TIFFRepresentation() set imgB to blankImage’s TIFFRepresentation() set chkWhite to (imgA’s isEqualToData:imgB) as boolean return chkWhite end checkImageIsWhite on getSizeOfImage(anNSImage) set aSize to anNSImage’s |size|() set aClass to class of aSize if aClass = record then copy aSize to theSize –To macOS 10.12.x else –macOS 10.13 or later set sizeX to (item 1 of item 2 of aSize) set sizeY to (item 2 of item 2 of aSize) set theSize to {width:sizeX, height:sizeY} end if return theSize end getSizeOfImage –指定サイズの画像を作成し、背景を指定色で塗る on drawImageWithFilledColor(anImage, fillColor) set aSize to getSizeOfImage(anImage) of me anImage’s lockFocus() set theRect to {{x:0, y:0}, {width:(width of aSize), height:(height of aSize)}} set theNSBezierPath to NSBezierPath’s bezierPath theNSBezierPath’s appendBezierPathWithRect:theRect fillColor’s |set|() theNSBezierPath’s fill() anImage’s unlockFocus() return anImage end drawImageWithFilledColor –aMaxValを最大値とする数値でNSColorを作成して返す on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer) set aRedCocoa to (redValue / aMaxVal) as real set aGreenCocoa to (greenValue / aMaxVal) as real set aBlueCocoa to (blueValue / aMaxVal) as real set aAlphaCocoa to (alphaValue / aMaxVal) as real set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa return aColor end makeNSColorFromRGBAval |
CIFilterとGPUImageで画像差分検出演算を比較 – AppleScriptの穴 says:
[…] タ」はGPUImageにしか存在しないのと、同処理はGPUImage.framework抜きで(AppleScriptだけで)2〜3倍高速な処理を行える部品「画像の空白判定 v3」を作成済みなので、GPUImageなしでもさほど困 […]
画像の空白判定 v4 – AppleScriptの穴 says:
[…] 以前にmacOS 10.12上で作成した「空白画像検出v3」は一般的に処理時間のかかる空白画像の検出(ヒストグラム計算してチェックする?)が高速に処理できます(GPUImageでヒストグラム計算 […]
画像の空白判定プログラムの検証 – AppleScriptの穴 says:
[…] (3)AppleScriptだけで書き直したもの Cocoaの機能を利用して画像の空白チェックを実現。元画像のコピーを作って、白くぬりつぶして、オリジナル画像との照合を行う。同じであれば空白画 […]