Steven
Steven6 min lugemist

OCR-ist piksliteni ilma varuvarianti kaotamata

GeekBye lõpetas su ekraanipiltide lugemise OCR-iga ja hakkas saatma päris pilti nägemismudelitele — mis on lihtne osa. Õpetlik osa on optimeerimine, mis jättis nägemiskasutajate jaoks OCR-i täielikult vahele, murdis vaikselt varuvariandi mitte-nägemismudelitele ja tuli tagasi keerata versiooniks, kus turvavõrk jookseb paralleelselt ega maksa midagi.

Arendus
AI
Desktop
GeekBye väljalasked
OCR-ist piksliteni ilma varuvarianti kaotamata

Igal äpil, mis lubab sul midagi ekraanipildina jäädvustada ja AI-lt selle kohta küsida, on ehtne teelahe. Sa võid käivitada OCR-i — eraldada teksti seadmes, saata pisike string ja pilt minema visata. Või võid saata pikslid — tarnida päris pildi ja lasta nägemisvõimelisel mudelil see lugeda. OCR on odav ja väike, aga pime kõigele, mis pole tekst: paigutusele, diagrammile, koodi taandele, punasele lainelisele joonele vea all. Pikslid säilitavad selle kõik, aga maksavad rohkem baite ja nõuavad mudelit, mis näeb. GeekBye alustas selle teelahkme OCR-poolel ja liikus v1.8.6 ja v1.8.7 jooksul üle piksli-poolele. See ümberlülitus on selle loo lihtne osa. Huvitav osa on optimeerimine, mis sellega kaasa tuli, murdis varuvariandi ja tuli tagasi keerata.

Enne: loe tekst, viska pilt minema

Kuni selle kaareni sai ekraanipildist vastus nii: sa vajutad jäädvustuse otseteed, klient käivitab OCR-i — Apple Vision raamistiku Swift VisionOCR binaari kaudu macOS-il, PowerShell skripti Windowsil, mõlemad ühe IOcrEngine abstraktsiooni taga — ja eraldatud tekst mähitakse stringi ning saadetakse backendile kui screenshotText. Pilt ise ei lahkunud kunagi masinast. See on tõhus, ja lihtteksti seina jaoks on see hea. Aga ekraanipilt on harva ainult tekst. See on kahe veeru paigutusega tööpakkumine, diagrammiga programmeerimisülesanne, virnajäljega viga, mille taane on vihje. OCR litsub selle kõik reahaaval transkriptsiooniks ja mudel vastab õhemale küsimusele, kui sa esitasid.

Ümberlülitus: saada pikslid (v1.8.6)

v1.8.6 lisas teise tee. Uus imageOptimizer.ts, ehitatud sharp-ile ja seega platvormiülene, teeb toorele ekraanipildile täpselt kolm asja:

sharp(pngFilePath).resize({ width: 1920, withoutEnlargement: true }).jpeg({ quality: 80 })

Muuda suurus kõige rohkem 1920px laiuseks (ära kunagi suurenda väiksemat jäädvustust), teisenda PNG JPEG-iks, kvaliteet 80. Väljund on toores base64, mille meediatüüp kantakse eraldi väljal, ja see sõidab backendisse kahes uues päringuväljas vana kõrval: screenshotImage ja screenshotImageMediaType, sõltumatult väljast screenshotText. Backend saab nüüd võtta vastu pildi, teksti või mõlemad.

See optimeerimissamm pole kosmeetiline. Toores ekraanipildi PNG on 3–8 MB; muudetud JPEG on umbes 200–500 KB. See on suurusjärgu võrra vähem üleslaadimist, ja see kärbib ka mudeli arvet — nägemismudelid jaotavad pildi selle resolutsiooni järgi paanideks ja võtavad tasu paani kaupa, nii et laiuse piiramine 1920-le ja ümberpakkimine vähendab otse nii baite juhtmel kui ka pildi tokeneid, mille eest sa maksad, ilma ekraanil oleva teksti loetavuse olulise kaotuseta.

Kuidas äpp üldse teab, kas mudel suudab näha? Backend kuulutab seda. /api/config vastus sai ai.vision ploki, mis on plaani järgi võtmestatud, ja klient puhverdab iga konfiguratsiooni pärimisel ühe tõeväärtuse — aiSupportsVision. Kui nägemine on toetatud, optimeeri pilt; kui pole, taandu OCR-ile. Nii et võimekus on backendi fakt, kliendi poolel puhverdatud, ja iga ekraanipildi otsus OCR käivitada või vahele jätta tehakse kliendis selle lipu põhjal. Backendile kuulub endiselt see, milline mudel tegelikult vastab.

Optimeerimine, mis oli liiga nutikas

Siin läheb õpetlikuks. Nägemistee esimene versioon käitas OCR-i ja pildi optimeerimist koos, paralleelselt. Siis pingutas üks "perf" commit selle kas-või-teiseks: kui plaan toetab nägemist, optimeeri pilt ja jäta OCR täielikult vahele; kui ei toeta, käivita OCR ja jäta pilditöö ära. Paberil on see ilmne võit — miks eraldada teksti, mida nägemismudel ei vaja? Muudatuste logi lausa reklaamib seda: "OCR is skipped when the AI can read the image directly."

Probleem on sõnas varuvariant. Backendil pole üht mudelit; sel on esmane ja varuvariant, ja varuvariant võib olla mitte-nägemismudel. Kujuta ette jada: sinu plaani esmane mudel toetab nägemist, nii et klient jätab uhkelt OCR-i vahele ja saadab ainult pildi. Esmane teenusepakkuja luksatab. Backend taandub ainult-teksti mudelile — millele nüüd ulatatakse pilt, mida see ei suuda lugeda, ja mitte mingit teksti, sest klient optimeeris teksti minema. Kiire tee oli vaikselt kustutanud turvavõrgu. Üleliigse kiire tee võib ohutult ära visata; ei saa visata seda, mille ümber kiire tee oli vaid otsetee.

Parandus: otsusta varuvariandi järgi, mitte esmase järgi

Parandus on osa, mida tasub varastada. See ei olnud minna tagasi OCR-i pideva käitamise juurde — see viskaks ära võidu tavajuhtumi jaoks. See oli teha vahelejätt sõltuvaks teisest võimekuse lipust: kas varuvariant toetab nägemist? Backendi konfiguratsioon sai fallbackVision ploki; klient puhverdab aiFallbackSupportsVision esimese lipu kõrval. Nüüd on otsusel neli haru:

  • Nii esmane kui varuvariant näevad → optimeeri pilt, jäta OCR vahele. Kiire, tavaline tee.
  • Esmane näeb, aga varuvariant on pime → optimeeri pilt ja käivita OCR, kindlustuseks varuvariandi jaoks.
  • Pildi optimeerimine viskab vea → OCR kui range varuvariant.
  • Plaanil pole üldse nägemist → ainult OCR.

Ja teine haru — kindlustusjuhtum — on koht, kus viimane käik loeb. OCR-i käitamine pärast pildi optimeerimist lisaks selle latentsuse otsa. Nii et mõlemad jooksevad koos ühe Promise.all all: pildi optimeerimine ja OCR kattuvad, ja OCR, mis eksisteerib vaid juhuks, kui backend seda vajab, lõpetab selle aja sees, mil pilti niikuinii optimeeriti. Turvavõrk on seinakella mõttes tasuta alati, kui seda ei kasutata. See on kogu trikk ühes lauses: hoia odav varuvariant alles, otsusta selle üle, kas su varuvariandi pakkuja seda tegelikult vajab, ja paralleeli see nii, et see ei maksa midagi, kuni see su päästab.

Kaks väiksemat lõksu, mille vahelejätt vallandas

Edasilükatud töö agressiivne vahelejätmine kipub esile tooma iga koha, mis vaikselt eeldas, et töö toimub. Kaks tulid välja sama nädala PR-ülevaatuses.

Järjekorra ummik. Ekraanipiltide järjekord luges elemendi "valmiks", kui sel oli OCR-tekst. Nägemis-ekraanipildid ei saanud nüüd kunagi OCR-teksti — disaini järgi — nii et terviklikkuse kontroll hasIncompleteOCR ootas igavesti ja järjekord jäi rippuma. Parandus defineerib "valmis" ümber kui on optimeeritud pilt või on OCR-tekst, mis on see, mida torustik nüüd tegelikult tagab.

Aegunud võimekuse lipp. aiSupportsVision on puhverdatud, mis tähendab, et see võib aeguda täpselt vales suunas: kasutaja logib välja või konfiguratsiooni pärimine ebaõnnestub ja klient jääb uskuma, et võib endiselt saata pilte mudelile, mis neid enam ei toeta. Lipp lähtestatakse nüüd väljalogimisel ja konfiguratsiooni pärimise ebaõnnestumisel — puhverdatud võimekusel peab olema plaan selleks, kui see, mida ta kirjeldab, kaob.

Paar sõna numbritest

Kuna ausus on selle sarja mõte: commit-sõnumid kannavad numbreid — pildi optimeerimine "~30ms" versus OCR "~100–150ms," koormad "~200–500 KB vs ~3–8 MB PNG" — ja need on usutavad, aga need on autori enda reasisesed hinnangud, mitte benchmarki väljund. Ajaloos pole kusagil mõõdetud otsast-otsani enne/pärast latentsust. Käsitle kiiruse argumenti kui mõistlikku disainikavatsust, mitte tõestatud tulemust. Koorma suuruse vähenemine on ainus number, mida saab usaldada, sest see tuleneb otse suuruse muutmisest ja ümberpakkimisest.

Kolm asja, mida see väljalase meile õpetas

  1. Pikslite saatmine lööb teksti lugemise — kui mudel suudab näha. OCR viskab ära kõik, mida ekraanipilt lisaks tähemärkidele edastab. Nägemismudel loeb paigutuse, diagrammi ja taande, ja optimeeritud JPEG teeb selle taskukohaseks. Aga see on panus võimekusele, mistõttu vajab see varuvarianti.
  2. Kiire tee saab optimeerida ära, turvavõrku mitte kunagi. OCR-i vahelejätt nägemiskasutajate jaoks oli esmase jaoks õige ja varuvariandi jaoks vale. Viga polnud vahelejätt; viga oli vahele jätta esmase võimekuse põhjal, kui just varuvariandi oma oli see, mis luges.
  3. Tasuta turvavõrk on paralleelitud turvavõrk. Otsusta kindlustustöö üle selle järgi, kas seda tegelikult vaja on, siis jooksuta seda kiire tee kõrval nii, et see kattub. Õigesti tehtud, ei maksa graatsiline degradeerumine midagi kuni päevani, mil see on ainus põhjus, miks su funktsioon veel töötab.

Eelmise peatüki jaoks v1 loos — klikiläbiva ülekatte kaks tõrkerežiimi (v1.8.5); ja kogu kaare jaoks tarkvara täiuslikkuseni tarnimise anatoomia.