Catturare un'immagine

Modulo di esempio: helloindicapture.

giveMeAnImage(): la visualizzazione senza alcun codice

HelloIndiCapture::HelloIndiCapture(...)
    : IndiModule(...)
{
    giveMeADevice("camera", "Camera", INDI::BaseDevice::CCD_INTERFACE);
    giveMeAnImage();
}

giveMeAnImage() costruisce da sola le proprietà image (sola lettura, elemento ElementImg) e imgparms (modalità di stretch, qualità jpeg) — nulla da dichiarare in JSON per questo. Ciò che il modulo deve ancora fornire è la capture stessa e il rinvio dell’immagine una volta ricevuta.

Richiedere la capture

void HelloIndiCapture::onExternalEvent(OST::ExtEvent event)
{
    if (event.ev != OST::ExtEvType::SV || event.prpkey != "actions" || event.eltkey != "capture") return;

    requestCapture(getString("devices", "camera"),
                    getFloat("shot", "exposure"),
                    getFloat("shot", "gain"),
                    getFloat("shot", "offset"));
    getEltBool("actions", "capture")->setValue(false, true);
}

requestCapture(device, esposizione, gain, offset) invia la richiesta al device; shot e actions sono due proprietà classiche dichiarate in JSON (vedi Il modulo più piccolo), nulla di specifico a INDI nella loro dichiarazione. requestCapture() attiva già da sola la ricezione dei BLOB su questo device (setBLOBMode(B_ALSO, ...)) — inutile chiamarla da soli prima.

Ricevere il BLOB

void HelloIndiCapture::updateProperty(INDI::Property property)
{
    IndiModule::updateProperty(property);

    if (QString(property.getDeviceName()) == getString("devices", "camera") && property.getType() == INDI_BLOB)
    {
        newBLOB(property);
    }
}

void HelloIndiCapture::newBLOB(INDI::PropertyBlob pblob)
{
    loadImageFromBlob(pblob, 64, [this]() { refreshPreview(); });
}

Catturare un’immagine è l’unico caso, in questa serie di esempi, che sovrascrive updateProperty() (protetto) piuttosto che onUpdateProperty() (pubblico) — vedi la panoramica. Il concatenamento IndiModule::updateProperty(property) sulla prima riga non è opzionale: è ciò che mantiene aggiornata la lista di valori di devices.* su una connessione. Dimenticarlo rompe questo aggiornamento silenziosamente, senza errore visibile.

Combinare la camera (updateProperty) e un altro device (onUpdateProperty)

Un modulo che pilota una camera e un focuser non deve scegliere tra i due meccanismi: IndiModule::updateProperty(property) chiama da sola onUpdateProperty(property) in uscita, per qualsiasi proprietà, non solo quelle del device che ha motivato la sovrascrittura. Sovrascrivere updateProperty() per intercettare i BLOB della camera (come sopra) e sovrascrivere onUpdateProperty() per seguire la posizione del focuser (come helloindiwrite) coesistono quindi senza conflitto nello stesso modulo, a patto che il concatenamento sopra sia fatto correttamente.

newBLOB() non è più un metodo virtuale di INDI::BaseClient

Sulle versioni recenti di libindi, un BLOB arriva come qualsiasi altra proprietà tramite updateProperty(), tipizzata INDI_BLOB — non c’è più un callback dedicato da sovrascrivere. newBLOB() sopra è un metodo privato ordinario del modulo, non una sovrascrittura; il dispatch manuale (property.getType() == INDI_BLOB) è l’unico modo attuale per attivarla, esattamente come fa dummy.cpp nel codice del core. Semplificare questo perché un modulo non debba più sovrascrivere updateProperty() da solo è evocato nell’issue ost/ostserver#271, non ancora deciso.

Ripubblicare l’immagine

void HelloIndiCapture::refreshPreview()
{
    if (!_image) return;

    QImage rawImage = _image->getRawQImage();
    atomicSaveJpeg(rawImage, getWebroot() + "/" + getModuleName() + ".jpeg");

    OST::ImgData data = _image->ImgStats();
    data.mUrlJpeg = getModuleName() + ".jpeg";
    getEltImg("image", "image")->setValue(data, true);
}

loadImageFromBlob() riempie _image (il frame residente del modulo, uno solo alla volta) e applica già lo stretch corrente; refreshPreview() non deve più fare altro che salvare un jpeg nel webroot e spingere il suo URL nell’elemento image. refreshPreview() viene anche richiamata automaticamente dal framework ogni volta che l’utente cambia imgparms.stretch — un modulo che ne ha bisogno scrive questo metodo solo una volta.

ImgStats() dà solo statistiche sui pixel, non l’HFR né il conteggio delle stelle

ImgStats() riempie solo i campi di OST::ImgData che si calcolano direttamente dai pixel grezzi: min/max/mean/stddev/median per canale, histogram, dimensioni. I campi HFRavg, HFRavgDev e starsCount esistono bene nella struttura, ma ImgStats() li lascia a zero.

Qualsiasi analisi d’immagine più avanzata (rilevamento di stelle, HFR, plate solving…) è un argomento di business, proprio a ciascun modulo, che non entra nel perimetro di questo framework — non è compito di questa documentazione coprirlo. Per ispirarsi a un’implementazione reale, vedi per esempio il modulo Focus del core (src/modules/focus/focus.cpp), che gestisce ciò tramite la propria istanza della classe Solver (libs/solver.cpp).