Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
OCRを使ってゲームのアイテムをデータ化する
Search
Kishikawa Katsumi
May 22, 2026
Programming
160
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
OCRを使ってゲームのアイテムをデータ化する
プロトタイプを製品にする技術
OCRを使ってゲームのアイテムをデータ化する
Kishikawa Katsumi
May 22, 2026
More Decks by Kishikawa Katsumi
See All by Kishikawa Katsumi
Running Swift without an OS
kishikawakatsumi
0
970
浮動小数の比較について
kishikawakatsumi
0
590
Automatic Grammar Agreementと Markdown Extended Attributes について
kishikawakatsumi
0
270
愛される翻訳の秘訣
kishikawakatsumi
3
480
Private APIの呼び出し方
kishikawakatsumi
3
1k
iOSでSVG画像を扱う
kishikawakatsumi
0
250
Build your own WebP codec in Swift
kishikawakatsumi
2
2.6k
iOSDC 2024 SMBファイル共有をSwiftで実装する
kishikawakatsumi
1
330
Enhancing Applications with Accessibility API
kishikawakatsumi
3
6.2k
Other Decks in Programming
See All in Programming
VibeCodingからAgenticWorkflowへ
starfish719
0
380
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
640
「寝てても仕事が進む」Claude Codeで組む第二の脳
tomoyafujita2016
0
320
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
470
<title><a id="</title>君はこのHTMLをパースできるか"></a></title> #雑LT_study
pizzacat83
0
140
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
210
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
4
390
自動化したのに回らないテスト運用の壁ーAI時代の品質責任と生産性
mfunaki
0
130
Go 1.27 における memory allocation の高速化
andpad
0
140
源内ハンズオン概要編
hideg
0
160
生成AI導入の「期待外れ」を乗り越える ー 開発フロー改革が目指す、真の組織変革
starfish719
0
4.2k
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
360
Featured
See All Featured
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
360
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
320
Google's AI Overviews - The New Search
badams
0
1.1k
How to Think Like a Performance Engineer
csswizardry
28
2.7k
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.5k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
330
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Evolving SEO for Evolving Search Engines
ryanjones
0
250
Navigating Team Friction
lara
192
16k
Docker and Python
trallard
47
4.1k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
440
Transcript
ϓϩτλΠϓΛʹ͢Δٕज़ LJTIJLBXBLBUTVNJ !LJTIJLBXBLBUTVNJ!IBDIZEFSNJP LJTIJLBXBLBUTVNJ 0$3ΛͬͯήʔϜͷΞΠςϜΛσʔλԽ͢Δ
None
ϓϩτλΠϓΛʹ͢Δٕज़ r ݸͷΞΠςϜΛਖ਼֬ʹσʔλԽ͢Δ r ̍ͭʹ͖ͭඵҎʹಡΈऔΕΔ r ήʔϜͷݴޠ͕ຊޠͱӳޠͷͲͪΒͰಡΈऔΕΔ
ϓϩτλΠϓΛʹ͢Δٕज़ r ݸͷΞΠςϜΛਖ਼֬ʹσʔλԽ͢Δ r ̍ͭʹ͖ͭඵҎʹಡΈऔΕΔ r ήʔϜͷݴޠ͕ຊޠͱӳޠͷͲͪΒͰಡΈऔΕΔ ਫ਼
ϓϩτλΠϓΛʹ͢Δٕज़ r ݸͷΞΠςϜΛਖ਼֬ʹσʔλԽ͢Δ r ̍ͭʹ͖ͭඵҎʹಡΈऔΕΔ r ήʔϜͷݴޠ͕ຊޠͱӳޠͷͲͪΒͰಡΈऔΕΔ ਫ਼
ϓϩτλΠϓΛʹ͢Δٕज़ r ݸͷΞΠςϜΛਖ਼֬ʹσʔλԽ͢Δ r ̍ͭʹ͖ͭඵҎʹಡΈऔΕΔ r ήʔϜͷݴޠ͕ຊޠͱӳޠͷͲͪΒͰಡΈऔΕΔ ਫ਼ ॊೈੑ
4BNQMF$PEF HJUIVCDPNLJTIJLBXBLBUTVNJ$BNFSB0$3
None
None
େ͖͞ ৭ छྨ ޮՌςΩετ
4UFQ3BX0$3 actor OCRRunner { private var busy = false func
process(_ cgImage: CGImage) async -> [RecognizedTextObservation]? { guard !busy else { return nil } busy = true defer { busy = false } var request = RecognizeTextRequest() request.recognitionLanguages = [ Locale.Language(identifier: "ja-JP"), Locale.Language(identifier: "en-US"), ] request.recognitionLevel = .accurate request.usesLanguageCorrection = false return try? await request.perform(on: cgImage) } }
None
4UFQ4UBCJMJUZ'JMUFS
let windowSize: Int = 5 let minHits: Int = 3
func updateStability(with results: [RecognizedTextObservation]) { let textsThisFrame = Set( results.compactMap { (observation) -> String? in let raw = observation.topCandidates(1).first?.string ?? "" let t = raw.trimmingCharacters(in: .whitespacesAndNewlines) return t.isEmpty ? nil : t } ) recentTextSets.append(textsThisFrame) if recentTextSets.count > windowSize { recentTextSets.removeFirst(recentTextSets.count - windowSize) } var counts: [String: Int] = [:] for set in recentTextSets { for t in set { counts[t, default: 0] += 1 } } let stable = counts.filter { $0.value >= minHits } stableTexts = Set(stable.keys) stableLines = stable .map { StableLine(text: $0.key, hits: $0.value) } .sorted { $0.hits == $1.hits ? $0.text < $1.text : $0.hits > $1.hits } } ϑϨʔϜͰճҎ্ग़ݱͨ͠ ςΩετΛ࠾༻͢Δɻ
None
Ԍ߈ܸྗ্ঢ Ԍ߈ܸྗ্ঢ ཕ߈ܸྗ্ঢ Ԍ߈ܸྗ্ঢ ཕ߈ܸྗ্ঢ ग़ܸ࣌ͷثʹ ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ
ཕ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ ཕ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ ཕ߈ܸྗΛՃ ϦϯάόοϑΝʹΑΔ҆ఆੑͷ্ /ϑϨʔϜத.ճҎ্ग़ͨςΩετΛ࠾༻͢Δ
Ԍ߈ܸྗ্ঢ Ԍ߈ܸྗ্ঢ ཕ߈ܸྗ্ঢ Ԍ߈ܸྗ্ঢ ཕ߈ܸྗ্ঢ ग़ܸ࣌ͷثʹ ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ
ཕ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ ཕ߈ܸྗΛՃ ग़ܸ࣌ͷثʹ ཕ߈ܸྗΛՃ ϦϯάόοϑΝʹΑΔ҆ఆੑͷ্ /ϑϨʔϜத.ճҎ্ग़ͨςΩετΛ࠾༻͢Δ
4UFQ30* 3FHJPOPG*OUFSFTU
ΨΠυͷൣғ͚ͩಡΈऔΔ ؔͳ͍ςΩετΛಡ·ͳ͍ɾ্
static let roiOnScreen = CGRect(x: 0.08, y: 0.30, width: 0.84,
height: 0.32) static let visionROI: NormalizedRect = { let r = roiOnScreen return NormalizedRect(x: r.minX, y: 1 - r.maxY, width: r.width, height: r.height) }() func process( _ cgImage: CGImage, roi: NormalizedRect ) async -> [RecognizedTextObservation]? { ... request.regionOfInterest = roi ... } 6*ͷ࠲ඪͷ7JTJPOGSBNFXPSLͷ ࠲ඪʹมͯ͠ηοτ ΨΠυͷൣғ͚ͩಡΈऔΔ ؔͳ͍ςΩετΛಡ·ͳ͍ɾ্
None
4UFQ.BTUFS.BUDIJOH
Ϛελʔσʔλͱর߹
func bestMatch(for input: String) -> (master: Master, distance: Int)? {
let n = normalize(input) var best: (Master, Int)? for (key, master) in normalizedKeys { if abs(key.count - n.count) > 5 { continue } let d = levenshtein(n, key) if best == nil || d < best!.1 { best = (master, d) } } return best } // डཧ: ڑ ≤ max(1, |master| × 0.3) ≒ score ≥ 0.70 let threshold = max(1, Int(Double(master.textJa.count) * 0.3)) guard match.distance <= threshold else { return nil } ฤूڑ -FWFOTIUFJO%JTUBODF ͰҰகΛఆ Ϛελʔσʔλͱর߹
None
ΞϧΰϦζϜ )BNNJOHڑ ܭࢉྔͱΈ ࠷ 903 QPQDPVOU ɻಉ͡͞ͷจࣈྻͰʮҟͳΔҐஔͷʯΛ͑Δ ࠾༻͠ͳ͔ͬͨཧ༝ 0$3ͷจࣈGSBNF͝ͱʹ༳ΕΔͨΊద༻ෆՄɻจࣈͰ͕͞ҧ͏ͱ͑ͳ͍ ΞϧΰϦζϜ
OHSBN+BDDBSEྨࣅ ܭࢉྔͱΈ ͍ ू߹ԋࢉ ɻจࣈ/HSBNू߹Λ࡞Γc"ˬ#cc"˫#cΛܭࢉ ࠾༻͠ͳ͔ͬͨཧ༝ จࣈॱংΛࣺͯΔͨΊʮ߈ܸྗ্ঢʯͱʮ্ঢ߈ܸྗʯΛ۠ผͰ͖ͳ͍ ΞϧΰϦζϜ %BNFSBV-FWFOTIUFJO ܭࢉྔͱΈ -FWFOTIUFJOͱ΄΅ಉ Θ͔ͣʹ͍ ɻ-FWFOTIUFJO ྡจࣈͷೖΕସ͑ΛίετͰڐ༰ ࠾༻͠ͳ͔ͬͨཧ༝ 0$3ͰUSBOTQPTJUJPOΑΓ७ਮͳஔޡΓ͕େͰɺԸܙ͕ബ͍ ΞϧΰϦζϜ +BSP8JOLMFS ܭࢉྔͱΈ -FWFOTIUFJOΑΓఆ͕খ͍͞ɻҰகจࣈ USBOTQPTJUJPO ڞ௨QSF fi YՃͰྨࣅΛग़͢ ࠾༻͠ͳ͔ͬͨཧ༝ ͍ਓ໊ɾॅॴ͚ʹ࠷దԽ͞ΕͨؔͰɺʙจࣈͷFGGFDUจͰ-FWFOTIUFJOͱͷ͕ࠩग़ʹ͍͘ ͦͷଞͷর߹ΞϧΰϦζϜ
ͦͷଞͷর߹ΞϧΰϦζϜ ΞϧΰϦζϜ 4ZN4QFMM ܭࢉྔͱΈ ࣄલܭࢉͰ࣮࣭0 MPPLVQɻNBTUFSΛʮFEJU≤Lͷશมܗʯʹల։ͨࣙ͠ॻΛQSFCVJME͠ɺೖྗల։ͯ͠IBTIিಥΛݕग़ ࠾༻͠ͳ͔ͬͨཧ༝ ڑ≤·Ͱ͔͠Ҿ͚ͣ͞มಈʹऑ͍ɻࣙॻల։Ͱ0 -
ͷϝϞϦு͕͋Γɺ݅نͰԸܙ͕͍͠ ΞϧΰϦζϜ #,USFF ܭࢉྔͱΈ ฏۉ0 MPH/ ఔͷۙ୳ࡧɻจࣈྻۭؒʹNFUSJDUSFFΛߏங͠ɺڑEҎͷͷΛͰߜΔ ࠾༻͠ͳ͔ͬͨཧ༝ ෦Ͱ݁ہ-FWFOTIUFJOΛݺͿɻ݅نͰΠϯσοΫεߏஙίετ͕ԸܙΛ্ճΔ ΞϧΰϦζϜ 4PVOEFY.FUBQIPOF ԻӆIBTI ܭࢉྔͱΈ ͍ ఆ࣌ؒ ɻൃԻྨࣅੑͰಉΫϥεΛ࡞ΓɺϋογϡҰகͰൺֱ ࠾༻͠ͳ͔ͬͨཧ༝ ӳޠԻӆ͚ͷࢉ๏Ͱ͋Γɺຊޠ$+,ʹద༻Ͱ͖ͳ͍ ΞϧΰϦζϜ จ຺ϞσϧຒΊࠐΈڑ #&35 ܭࢉྔͱΈ େ෯ʹ͍ ेNTΫΤϦ ɻจࣈྻΛߴ࣍ݩϕΫτϧʹຒΊࠐΈɺDPTJOFڑͰྨࣅΛܭࢉ ࠾༻͠ͳ͔ͬͨཧ༝ ϦΞϧλΠϜಈըʹॏ͗͢ΔɻϞσϧ͕NBTUFSͷEPNBJOޠኮɺಛʹήʔϜޠΛΒͳ͍
ೖྗσʔλΛΩϨΠʹ͢Δࡉ͔͍ r Χˠྗ̍ͭΧλΧφͷΧΛࣈͷྗ ͔ͪΒ ʹஔ͖͑Δ ‣ ߈ܸʮྗʯͳͲܾ·ͬͨύλʔϯʹ͍ͭͯ r શ֯ɾ֯Λଗ͑Δ r
ۭനΛআڈ͢Δ Ϛονϯάͷલʹਖ਼نԽͯ͠ϊΠζΛআڈ͢Δ
ೝࣝΛ্ͤ͞Δࡉ͔͍ 0xD5D5EBF7EAD5D5EB ݩը૾ 9×8 grayscale 8×8 bit pattern 64-bit hash
E)BTIΛϋϛϯάڑͰൺֱͯ͠ྨࣅͷϑϨʔϜΛແࢹ͢Δ
None
·ͱΊ r Ϛελʔσʔλʢਖ਼ղͷఆٛʣΛ͑Δ r ϑϨʔϜ୯ҐͰޡೝࣝΛϑΟϧλʔ͢Δ ‣ .VMUJGSBNF$POTFOTVT r ࣄલʹೖྗΛΫϦʔϯʹ͢Δ ‣
ςΩετͷਖ਼نԽ ‣ Α͋͘ΔޡೝࣝΛஔ r ڍಈΛܾఆతɾ؍ଌՄೳʹ͢Δ ߴ͍࣭Ͱ࠶ݱੑͷ͋Δ݁ՌΛग़ྗ͢ΔͨΊͷ
3FTPVSDFT r IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJ$BNFSB0$3 r IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJ3FMJD'PSHF r IUUQTBQQTBQQMFDPNVTBQQSFMJDGPSHFJE r IUUQTSFMJDGPSHFQBHFTEFW