cardstream
Over

Het gaat erom één keer per kaart te vragen, niet één keer per frame

Kaartherkenning werkt al. Richt een goed model op een verzamelkaart en de naam, de set en het nummer komen in ongeveer een halve seconde terug. Pas als je dat op video loslaat, loopt het spaak. Niet technisch, maar door het pure volume.

Video is vooral herhaling. Een kaart die je vier seconden omhooghoudt bij vijftien frames per seconde, levert zestig beelden van één kaart op, en wie een identificatie-endpoint zestig keer iets vraagt, krijgt zestig keer hetzelfde antwoord. Elke naïeve streamingpipeline doet precies dat. Daarom heeft streamanalyse de naam iets te zijn dat alleen een platform met investeerders kan draaien.

cardstream bestaat om die herhaling te schrappen. Het is een kleine toestandsmachine tussen je video en de herkenningsaanroep, bijna helemaal opgebouwd uit signalen die ze zelf afleidt: is het beeld tot rust gekomen, is dit dezelfde kaart als daarnet, is er überhaupt een kaart in beeld? De aanroep gaat uit wanneer een echt nieuwe kaart stilligt, en anders niet. Al het andere draait op hardware die je al hebt.

De rekensom

Eén aanroep per kaart, niet per frame

Eén uur show bij vijftien frames per seconde (de bovengrens waarop het project is getest) en ongeveer honderd verschillende kaarten voor de lens. Twee manieren om precies dezelfde video te analyseren:

Frame voor frame 54.000

identificatie-aanroepen: één per frame, waarvan de meeste een vraag opnieuw stellen die een ogenblik eerder al was beantwoord.

Met cardstream ~100

identificatie-aanroepen: één per nieuwe kaart, wat je framerate ook is en hoe lang je elke kaart ook omhooghoudt.

Ongeveer 540× minder aanroepen, en dat verschil is geen cijfertruc: het is het hele ontwerp. Houd een kaart stil en de teller beweegt niet. Laat de camera sneller lopen en de teller beweegt niet. Het aantal aanroepen volgt de kaarten die je laat zien, niet de uren die je streamt.

Beheer

Wie het draaiende houdt

Gebouwd door mensen die herkenning in productie hebben

cardstream komt van het team achter de herkenning van verzamelobjecten bij Ximilar. De identificatie-endpoints die het aanroept, zijn dezelfde die wij in productie draaien. De client en de dienst worden dus door dezelfde mensen onderhouden.

Het werk gebeurt in het openbaar

Het volledige pakket staat op GitHub: de toestandsmachine, beide deploymentvarianten en de browser-UI. Niets van de beslislogica zit verstopt achter een dienst waar je niet in kunt kijken.

Getest zonder netwerk

De hele suite draait offline, met fakes in de plaats van de modelbestanden en de HTTP-aanroepen. Fork de repo op een laptop zonder API-sleutel en je ziet nog steeds of je iets hebt stukgemaakt.

Principes

Wat de code niet mag vergeten

  1. Eén aanroep per kaart, niet per frame

    Elke ontwerpbeslissing begint hier. Als een signaal lokaal te berekenen is, komt het vóór de ene aanroep die de machine verlaat.

  2. Eén exemplaar van de beslislogica

    Wanneer er een aanroep komt, staat in precies één module. Niet verspreid over een driver, een transportlaag en een UI waar drie mensen het op drie manieren kunnen veranderen: één bestand, in één keer te lezen.

  3. Drivers blijven dun

    Scheduling, logging en I/O horen bij de driver; wat als een nieuwe kaart telt, hoort bij de engine. Verander je hoe de frames binnenkomen, dan verandert er nooit ongemerkt wanneer een aanroep uitgaat.

  4. Niets blokkeert de frame-loop

    Decoderen, detectie, HTTP en schijf draaien allemaal buiten de loop. Een trage identificatie kost je één frame; je stream loopt er nooit een groeiende achterstand door op.

  5. Geen lock-in bij een aanbieder, ook niet bij ons

    De identificatie-aanroep is één verwisselbare stap achter een kleine interface. Elk filter, elke begrenzing en elke cache blijft werken als je hem ergens anders naartoe laat wijzen.

  6. Standaard zelf gehost

    Jouw hardware, jouw sleutel, jouw video. In client-side modus is één uitsnede per nieuwe kaart het enige dat de machine verlaat, tenzij je ervoor kiest de show in je eigen Ximilar-account op te slaan.

Veldnotities

Wat de kaarten ons hebben geleerd

Een deel hiervan gaat zo tegen je intuïtie in dat we het opschrijven waar de volgende het vindt: in de repo, naast de code waarvoor het geldt.

Een behulpzame hint kan je nauwkeurigheid kosten

Vertel je het endpoint uit welk spel een kaart komt, dan schakelt het zijn eigen schriftclassifier uit en valt het terug op latin. Een Japanse kaart matcht dan stilletjes met zijn Engelse druk. Gemeten, niet gegokt, en daarom geldt een spel instellen zonder schrift als een fout.

Een iets ruimere uitsnede matcht beter

Strak rond de kaart snijden ziet er goed uit en presteert slechter. De uitsnede die wordt verstuurd, heeft bewust een marge, want een beetje context wint het van een schone rand.

Herstellen is beter dan doen alsof

Blijft een aanroep langer hangen dan zijn timeout, dan maakt de pipeline zichzelf los en gaat hij verder, in plaats van te wachten op een resultaat dat misschien nooit komt. Die beperking staat beschreven in de repo en is niet weggemoffeld.

Broncode en bijdragen

Lees het, draai het, stuur patches

De repository is openbaar: één installeerbaar Python-pakket met onafhankelijke extras en een browser-UI. De gewichten van de locator en van het embeddingmodel zijn van ons, getraind en samen met de releases gepubliceerd onder Apache-2.0. Issues en pull requests zijn welkom, vooral meldingen uit echte shows: die brengen dingen aan het licht die geen testopstelling laat zien.

De herkenning zelf is het endpoint van Ximilar en dat blijft zo: dat is het deel met de kaartendatabase erachter. Alles eromheen mag je aanpassen.

Open source. Zelf gehost. Jouw stream, jouw stack.

Geen platform ertussen, geen licentie per gebruiker: koppel de Ximilar-API of je eigen identificatiesysteem, en cardstream roept het één keer per nieuwe kaart aan in plaats van één keer per frame. Of je nu breaks doet op Whatnot, een show draait zoals op Fanatics Live of live verkoopt via je eigen opstelling: draai cardstream vanavond nog, op de hardware die je al hebt.