20260526

[AppleScript]画像ファイルの中の文字で検索+OCR結果をFinderコメントに入れる

[AppleScript]画像ファイルの中の文字で検索+OCR結果をFinderコメントに入れる

NOTE記事一覧ですnote.com

【スクリプトエディタで開く】 |

画像ファイルの中の文字で検索+コメント入れ.scpt.scpt
ソース
001#!/usr/bin/env osascript
002#coding: utf-8
003----+----1----+----2----+-----3----+----4----+----5----+----6----+----7--
004(*
005
006画像ファイルをOCRして
007OCRの内容=画像内のテキストを検索します
008マッチしたファイルパスをダイアログに戻して
009クリップボードにコピー
010
011プレビューで開くか選べます
012
0131画像ファイルを検索するのに約0.1秒-0.3前後かかります
014
015100ファイルあると約12-5秒かかります
016
017
018OCR結果の先頭から500文字までをFinderコメントに入れて
019通常検索でもスポットライト検索の結果に出るようにする処理を入れました
020スポットライトへのmdimportは実施しないので
021スポットライト検索結果に出るか?は、利用者の環境依存があります。
022
023
024
025
026com.cocolog-nifty.quicktimer.icefloe *)
027----+----1----+----2----+-----3----+----4----+----5----+----6----+----7--
028use AppleScript version "2.8"
029use framework "Foundation"
030use framework "AppKit"
031use framework "Vision"
032#使わないことにした
033#   use framework "NaturalLanguage"
034use framework "CoreFoundation"
035use framework "UniformTypeIdentifiers"
036use scripting additions
037
038property refMe : a reference to current application
039
040#############################
041#ダイアログ
042set appFileManager to refMe's NSFileManager's defaultManager()
043set ocidURLsArray to (appFileManager's URLsForDirectory:(refMe's NSDesktopDirectory) inDomains:(refMe's NSUserDomainMask))
044set ocidDesktopDirPathURL to ocidURLsArray's firstObject()
045set aliasDefaultLocation to (ocidDesktopDirPathURL's absoluteURL()) as alias
046set listUTI to {"public.image", "public.tiff", "public.jpeg", "public.png"} as list
047
048set strMsg to ("画像ファイルを選択してください" & return & "画像の中の文字をOCRしてテキスト検索します") as text
049set strPrompt to ("画像ファイルを選択してください" & return & "画像の中の文字をOCRしてテキスト検索します") as text
050try
051   tell application "SystemUIServer"
052      activate
053      set listAliasFilePath to (choose file strMsg with prompt strPrompt default location aliasDefaultLocation of type listUTI with invisibles and multiple selections allowed without showing package contents) as list
054   end tell
055on error strErrMes number numErrNo
056   log strErrMes & numErrNo
057   return false
058end try
059if listAliasFilePath is {} then
060   return false
061end if
062
063
064##############################
065#   クリップボードの中身取り出し
066set appPasteboard to refMe's NSPasteboard's generalPasteboard()
067set ocidPastBoardTypeArray to appPasteboard's types()
068set boolContain to ocidPastBoardTypeArray's containsObject:(refMe's NSStringPboardType)
069if (boolContain as boolean) is true then
070   set ocidString to appPasteboard's stringForType:(refMe's NSStringPboardType)
071   set strDefaultAnswer to ocidString as text
072else
073   set strDefaultAnswer to ("文字入力10文字程度") as text
074end if
075#ダイアログ保護のため50文字まで
076set numCntChar to (count of every character of strDefaultAnswer) as integer
077if numCntChar > 50 then
078   set strDefaultAnswer to (text 1 through 50 of strDefaultAnswer) as text
079end if
080
081##############################
082#   ダイアログ
083set strIconFilePath to ("/System/Library/UserNotifications/Bundles/com.apple.identityservicesd.firewall.bundle/Contents/Resources/Apple Care_Mac.icns") as text
084set aliasIconFilePath to (POSIX file strIconFilePath) as alias
085set strMsg to ("テキストを入力してください" & return & "語句検索します") as text
086set strTitle to ("入力してください") as text
087set strOK to ("OK") as text
088set strCancel to ("キャンセル") as text
089set listBotton to {strOK, strCancel} as list
090try
091   tell application "System Events"
092      activate
093      set recordResult to (display dialog strMsg with title strTitle default answer strDefaultAnswer buttons listBotton default button strOK cancel button strCancel with icon aliasIconFilePath giving up after 120 without hidden answer) as record
094   end tell
095on error strErrMsg number numErrNo
096   log strErrMsg & numErrNo
097   return false
098end try
099
100set strResponseButton to (button returned of recordResult) as text
101set boolGaveUp to (gave up of recordResult) as boolean
102if boolGaveUp is true then
103   log "時間切れ"
104   return false
105else if strResponseButton is strOK then
106   set strReturnedText to (text returned of recordResult) as text
107end if
108#スタート計測
109set ocidStart to refMe's CFAbsoluteTimeGetCurrent()
110
111##############################
112#戻り値整形
113set ocidOrgStrings to (refMe's NSString's stringWithString:(strReturnedText))
114#NFC処理
115set ocidOrgStringsNFC to ocidOrgStrings's precomposedStringWithCanonicalMapping()
116#改行をLFに強制
117set ocidReplacedStrings to (ocidOrgStringsNFC's stringByReplacingOccurrencesOfString:(linefeed) withString:(""))
118set ocidReplacedStrings to (ocidReplacedStrings's stringByReplacingOccurrencesOfString:(return) withString:(""))
119set ocidSearchStrings to (ocidReplacedStrings's stringByReplacingOccurrencesOfString:(tab) withString:(""))
120
121##############################
122#本処理
123#ヒットしたファイルのURL格納用
124set ocidHitsPathArrayM to refMe's NSMutableArray's alloc()'s init()
125set ocidDistancePathArrayM to refMe's NSMutableArray's alloc()'s init()
126
127
128repeat with itemAliasFilePath in listAliasFilePath
129   set aliasFilePath to itemAliasFilePath as alias
130   set strFilePath to (POSIX path of aliasFilePath) as text
131   set ocidFilePathStr to (refMe's NSString's stringWithString:(strFilePath))
132   set ocidFilePath to ocidFilePathStr's stringByStandardizingPath()
133   set ocidFilePathURL to (refMe's NSURL's alloc()'s initFileURLWithPath:(ocidFilePath) isDirectory:(false))
134   #NSDATAで読み込み
135   set ocidOption to (refMe's NSDataReadingMappedIfSafe)
136   set listReadData to (refMe's NSData's alloc()'s initWithContentsOfURL:(ocidFilePathURL) options:(ocidOption) |error|:(reference))
137   set ocidReadData to (first item of listReadData)
138   #NSBitmapImageRepに
139   set ocidImgRep to (refMe's NSBitmapImageRep's imageRepWithData:(ocidReadData))
140   #イメージサイズ
141   set ocidImageWpx to ocidImgRep's pixelsWide()
142   set ocidImageHpx to ocidImgRep's pixelsHigh()
143   #半分Pxサイズ
144   set numHalfWpx to doGetHalfNo(ocidImageWpx)
145   set numHalfHpx to doGetHalfNo(ocidImageHpx)
146   #解放
147   set ocidImgRep to (missing value)
148   #最小文字サイズ
149   set numMinPt to 8 as integer
150   set intTextHeight to (numMinPt / numHalfHpx) as number
151   #####OCR
152   #VNImageRequestHandler's
153   set ocidHandler to (refMe's VNImageRequestHandler's alloc()'s initWithData:(ocidReadData) options:(missing value))
154   #VNRecognizeTextRequest's
155   set ocidRequest to refMe's VNRecognizeTextRequest's alloc()'s init()
156   set ocidOption to (refMe's VNRequestTextRecognitionLevelAccurate)
157   (ocidRequest's setRecognitionLevel:(ocidOption))
158   (ocidRequest's setMinimumTextHeight:(intTextHeight))
159   (ocidRequest's setAutomaticallyDetectsLanguage:(true))
160   (ocidRequest's setRecognitionLanguages:{"en", "ja"})
161   (ocidRequest's setUsesLanguageCorrection:(false))
162   #results
163   (ocidHandler's performRequests:(refMe's NSArray's arrayWithObject:(ocidRequest)) |error|:(reference))
164   set ocidResponseArray to ocidRequest's results()
165   set ocidFirstOpinionString to (refMe's NSMutableString's alloc()'s init())
166   #missing value対策
167   if ocidResponseArray = (missing value) then
168      (ocidFirstOpinionString's appendString:("テキストは認識されませんでした"))
169   else
170      #戻り値の数だけ々
171      repeat with itemArray in ocidResponseArray
172         #候補数指定 1-10 ここでは2種の候補を戻す指定
173         set ocidRecognizedTextArray to (itemArray's topCandidates:(1))
174         set ocidFirstOpinion to ocidRecognizedTextArray's firstObject()
175         (ocidFirstOpinionString's appendString:(ocidFirstOpinion's |string|()))
176         (ocidFirstOpinionString's appendString:(return))
177      end repeat
178   end if
179   set ocidOpinionString to (ocidFirstOpinionString's stringByReplacingOccurrencesOfString:(return) withString:(""))
180   #最初から500文字までをコメントに入れる
181   set numStrLength to ocidOpinionString's |length|()
182   if numStrLength < 500 then
183      set numTrimIndex to numStrLength as integer
184   else
185      set numTrimIndex to 500 as integer
186   end if
187   #500文字までを設定テキストとして後処理に回す
188   set ocidTrimRange to refMe's NSRange's NSMakeRange(0, numTrimIndex)
189   set ocidTrimString to (ocidOpinionString's substringWithRange:(ocidTrimRange))
190   set strMetaString to ocidTrimString as text
191   #########################
192   #コメント入れ
193   tell application "Finder"
194      tell file aliasFilePath
195         set comment to strMetaString
196      end tell
197   end tell
198   #########判定
199   set boolContain to (ocidFirstOpinionString's localizedStandardContainsString:(ocidSearchStrings))
200   if boolContain is true then
201      (ocidHitsPathArrayM's addObject:(ocidFilePath))
202   end if
203   #########判定
204   #日本語対応していないので処理はするけど使わない
205   (*
206   set appNLLang to (refMe's NLEmbedding's sentenceEmbeddingForLanguage:(refMe's NLLanguageJapanese))
207   if appNLLang = (missing value) then
208      set appNLLang to (refMe's NLEmbedding's sentenceEmbeddingForLanguage:(refMe's NLLanguageEnglish))
209   end if
210   set numDistance to (appNLLang's distanceBetweenString:(ocidFirstOpinionString) andString:(ocidSearchStrings) distanceType:(refMe's NLDistanceTypeCosine))
211   if (numDistance as number) < 1.5 then
212      (ocidDistancePathArrayM's addObject:(ocidFilePath))
213   end if
214*)
215end repeat
216
217set ocidEnd to refMe's CFAbsoluteTimeGetCurrent()
218set strTime to (ocidEnd - ocidStart) as text
219
220
221#################################
222#ソート
223set ocidDescriptor to refMe's NSSortDescriptor's sortDescriptorWithKey:("self") ascending:(yes) selector:("localizedStandardCompare:")
224set ocidDescriptorArray to refMe's NSArray's arrayWithObject:(ocidDescriptor)
225set ocidSortedPathArray to ocidHitsPathArrayM's sortedArrayUsingDescriptors:(ocidDescriptorArray)
226set ocidJoinText to ocidSortedPathArray's componentsJoinedByString:(linefeed)
227set strJoinText to ocidJoinText as string
228
229
230##############################
231#   ダイアログ
232set strIconFilePath to ("/System/Library/UserNotifications/Bundles/com.apple.identityservicesd.firewall.bundle/Contents/Resources/Apple Care_Mac.icns") as text
233set aliasIconFilePath to (POSIX file strIconFilePath) as alias
234set strMsg to ("マッチしたファイルパスです" & return & "コピーできます") as text
235set strTitle to ("処理の戻り値です") as text
236set strOK to ("プレビューで画像を開く") as text
237set strCancel to ("キャンセル") as text
238set strCopyToClipboard to ("クリップボードにコピー") as text
239set listBotton to {strCopyToClipboard, strCancel, strOK} as list
240try
241   tell application "System Events"
242      activate
243      set recordResult to (display dialog strMsg with title strTitle default answer strJoinText buttons listBotton default button strCopyToClipboard cancel button strCancel with icon aliasIconFilePath giving up after 20 without hidden answer) as record
244   end tell
245on error strErrMsg number numErrNo
246   log strErrMsg & numErrNo
247   return false
248end try
249try
250   tell application "System Events" to quit
251end try
252set strResponseButton to (button returned of recordResult) as text
253set boolGaveUp to (gave up of recordResult) as boolean
254if boolGaveUp is true then
255   log "時間切れ"
256   return false
257end if
258set strResponseText to (text returned of recordResult) as text
259if strResponseButton is strOK then
260   set listAliasFilePath to {} as list
261   repeat with itemPath in ocidSortedPathArray
262      set strItemPath to itemPath as text
263      set aliasItemPath to (POSIX file strItemPath) as alias
264      set end of listAliasFilePath to aliasItemPath
265   end repeat
266   
267   tell application id "com.apple.Preview"
268      activate
269      open listAliasFilePath
270   end tell
271   return true
272else if strResponseButton is strCopyToClipboard then
273   #ペーストボードに格納する
274   set appPasteboard to refMe's NSPasteboard's generalPasteboard()
275   set ocidText to (refMe's NSString's stringWithString:(strResponseText))
276   appPasteboard's clearContents()
277   set boolDone to appPasteboard's setString:(ocidText) forType:(refMe's NSPasteboardTypeString)
278   return boolDone
279end if
280
281return 0
282
283
284
285#################################
286#偶数を作る
287to doGetHalfNo(argInt)
288   set numInt to argInt as number
289   set numRound to ((numInt / 2) + 0.5) as integer
290   set numOddEven to (numRound mod 2) as integer
291   if numOddEven = 1 then
292      set numRound to (numRound - 1) as integer
293   end if
294   return numRound
295end doGetHalfNo
AppleScriptで生成しました