Adobe Illustrator書類上にある、QRコードのオブジェクトをクリップボード経由でNSImageに変換してQRコード認識するAppleScriptです。QRコード認識には、OS内蔵のCoreImageの機能を使っています。
# 実行前にAI書類上のQRコードオブジェクトを選択してコピー(Command-C)を実行してあることを前提条件としています。QRコードっぽいものであれば、別にAI書類上のオブジェクトでなくてもかまいません
昨日のScriptで、「そういえばなんでZXingObjCを使っているんだっけ?」と疑問を抱きました。理由ははっきりしています。CIDetectorではそのまま認識させてみたら認識しなかったためです。
QRコードやJANコード画像認識ソフトウェアは、割といろいろ条件がそろわないと認識してくれません。いわく、画像の大きさ、画像の解像度、画像の向き、コントラスト比、その他いろいろ。
スマホのカメラで認識する際にはユーザーが認識するまでいろいろ条件を変えて試行錯誤するので、ソフトウェアの力だけで認識させているわけではありません。いわば、人間をスマホの周辺機器として使用している状態です。
一方、静止画をソフトウェアで認識処理するさいには、人間が行っているフィードバック動作をソフトウェア側で行わなくてはなりません。
macOS内蔵のCIDetectorは、どうやら画像サイズに敏感なようで、いろいろリサイズして認識するかどうかチェックしなくてはなりません。逆に、最初からQRコードではないとわかっている画像を、しつこくQRコード認識し直しても時間の無駄です。
とりあえず、認識対象を2倍から4倍まで拡大して認識をリトライしています。実際にやってみたら、1倍では認識しなくても2倍で認識してくれました。実際に処理するデータ次第ですが、4倍まで試さなくても大丈夫なケースも多いことでしょう。
AppleScript名:rocogClipAsQR_v2.scptd |
— Created 2019-07-28 by Takaaki Naganoya — 2019 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "AppKit" use framework "CoreImage" property CIImage : a reference to current application’s CIImage property NSImage : a reference to current application’s NSImage property CIDetector : a reference to current application’s CIDetector property NSZeroRect : a reference to current application’s NSZeroRect property NSDictionary : a reference to current application’s NSDictionary property NSPasteboard : a reference to current application’s NSPasteboard property NSCompositeCopy : a reference to current application’s NSCompositeCopy property NSGraphicsContext : a reference to current application’s NSGraphicsContext property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep property CIDetectorAccuracy : a reference to current application’s CIDetectorAccuracy property CIDetectorTypeQRCode : a reference to current application’s CIDetectorTypeQRCode property CIDetectorAccuracyHigh : a reference to current application’s CIDetectorAccuracyHigh property NSImageInterpolationNone : a reference to current application’s NSImageInterpolationNone property CIDetectorImageOrientation : a reference to current application’s CIDetectorImageOrientation property NSCalibratedRGBColorSpace : a reference to current application’s NSCalibratedRGBColorSpace set aRes to recogPasteboardASQR() of me –クリップボードの内容をNSImageに on recogPasteboardASQR() set aNSIMage to getClipboardASImage() of me if aNSIMage = false then return false set aRes to recogNSImageAsQRcodes(aNSIMage) of me –11: kBarcodeFormatQRCode if aRes is not equal to {} then return aRes –ループでQRコードとおぼしきイメージを拡大しつつQRコード認識 repeat with i from 2 to 4 –ここはチューニングの余地がある set bNSIMage to (my resizeNSImageWithoutAntlialias:aNSIMage toScale:(i as real)) set aRes to recogNSImageAsQRcodes(bNSIMage) of me if aRes is not equal to {} then return aRes end repeat return false end recogPasteboardASQR –NSImageをバーコードとして認識する on recogNSImageAsQRcodes(aNSIMage) set imageRef to convNSImageToCIimage(aNSIMage) of me — 検出器のオプションを NSDictonary で作成 set optDic1 to NSDictionary’s dictionaryWithObject:(CIDetectorAccuracyHigh) forKey:(CIDetectorAccuracy) set faceDetector to CIDetector’s detectorOfType:(CIDetectorTypeQRCode) context:(missing value) options:optDic1 — QRコードの検出を行う際のオプションを NSDictonary で作成 set optDic2 to NSDictionary’s dictionaryWithObject:(CIDetectorImageOrientation) forKey:"Orientation" — QRコード検出を実行 set faceArray to faceDetector’s featuresInImage:imageRef options:optDic2 set fList to {} — 検出されたQRコードの位置とサイズをログに出力 repeat with i from 1 to (count of faceArray) set face to item i of faceArray set bRec to (face’s messageString()) as string –set cRec to retURLdecodedStrings(bRec) of me –URLエンコード対策 set the end of fList to bRec end repeat return fList end recogNSImageAsQRcodes — クリップボードの内容をNSImageとして取り出して返す on getClipboardASImage() set theNSPasteboard to NSPasteboard’s generalPasteboard() set anArray to theNSPasteboard’s readObjectsForClasses:({NSImage}) options:(missing value) if anArray = missing value or (anArray as list) = {} then return false set aRes to anArray’s objectAtIndex:0 return aRes end getClipboardASImage — on convNSImageToCIimage(aNSIMage) set tiffDat to aNSIMage’s TIFFRepresentation() set aRep to NSBitmapImageRep’s imageRepWithData:tiffDat set newImg to CIImage’s alloc()’s initWithBitmapImageRep:aRep return newImg end convNSImageToCIimage –NSImageを指定倍率で拡大(アンチエイリアス解除状態で) on resizeNSImageWithoutAntlialias:aSourceImg toScale:imgScale set aSize to aSourceImg’s |size|() set aWidth to (aSize’s width) * imgScale set aHeight to (aSize’s height) * imgScale set aRep to NSBitmapImageRep’s alloc()’s initWithBitmapDataPlanes:(missing value) pixelsWide:aWidth pixelsHigh:aHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(NSCalibratedRGBColorSpace) bytesPerRow:0 bitsPerPixel:0 set newSize to {width:aWidth, height:aHeight} aRep’s setSize:newSize NSGraphicsContext’s saveGraphicsState() set theContext to NSGraphicsContext’s graphicsContextWithBitmapImageRep:aRep NSGraphicsContext’s setCurrentContext:theContext theContext’s setShouldAntialias:false theContext’s setImageInterpolation:(NSImageInterpolationNone) aSourceImg’s drawInRect:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) fromRect:(NSZeroRect) operation:(NSCompositeCopy) fraction:(1.0) NSGraphicsContext’s restoreGraphicsState() set newImg to NSImage’s alloc()’s initWithSize:newSize newImg’s addRepresentation:aRep return newImg end resizeNSImageWithoutAntlialias:toScale: |