Capturer une image
Module d’exemple : helloindicapture.
giveMeAnImage() : l’affichage sans aucun code
HelloIndiCapture::HelloIndiCapture(...)
: IndiModule(...)
{
giveMeADevice("camera", "Camera", INDI::BaseDevice::CCD_INTERFACE);
giveMeAnImage();
}giveMeAnImage() construit à elle seule les propriétés image (lecture seule, élément ElementImg) et imgparms (mode de stretch, qualité jpeg) — rien à déclarer en JSON pour ça. Ce que le module doit encore fournir, c’est la capture elle-même et le renvoi de l’image une fois reçue.
Demander 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, exposition, gain, offset) envoie la demande au device ; shot et actions sont deux propriétés classiques déclarées en JSON (voir Le plus petit module), rien de spécifique à INDI dans leur déclaration. requestCapture() active déjà lui-même la réception des BLOBs sur ce device (setBLOBMode(B_ALSO, ...)) — inutile de l’appeler soi-même avant.
Recevoir le 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(); });
}Capturer une image est le seul cas, dans cette série d’exemples, qui surcharge updateProperty() (protégé) plutôt que onUpdateProperty() (public) — voir la vue d’ensemble. Le chaînage IndiModule::updateProperty(property) en première ligne n’est pas optionnel : c’est lui qui maintient la liste de valeurs de devices.* à jour sur une connexion. L’oublier casse ce rafraîchissement silencieusement, sans erreur visible.
Un module qui pilote une caméra et un focuser n’a pas à choisir entre les deux mécanismes : IndiModule::updateProperty(property) appelle lui-même onUpdateProperty(property) en sortie, pour toute propriété, pas seulement celles du device qui a motivé la surcharge. Surcharger updateProperty() pour intercepter les BLOBs de la caméra (comme ci-dessus) et surcharger onUpdateProperty() pour suivre la position du focuser (comme helloindiwrite) coexistent donc sans conflit dans le même module, tant que le chaînage ci-dessus est bien fait.
Sur les versions récentes de libindi, un BLOB arrive comme n’importe quelle autre propriété via updateProperty(), typée INDI_BLOB — il n’y a plus de callback dédié à surcharger. newBLOB() ci-dessus est une méthode privée ordinaire du module, pas une surcharge ; le dispatch manuel (property.getType() == INDI_BLOB) est le seul moyen actuel de la déclencher, exactement comme le fait dummy.cpp dans le code du core. Simplifier ça pour qu’un module n’ait plus besoin de surcharger updateProperty() lui-même est évoqué dans l’issue ost/ostserver#271, pas encore tranché.
Republier l’image
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() remplit _image (la trame résidente du module, une seule à la fois) et applique déjà le stretch courant ; refreshPreview() n’a plus qu’à sauver un jpeg dans le webroot et pousser son URL dans l’élément image. refreshPreview() est aussi rappelée automatiquement par le framework chaque fois que l’utilisateur change imgparms.stretch — un module qui en a besoin n’écrit cette méthode qu’une seule fois.
ImgStats() remplit uniquement les champs de OST::ImgData qui se calculent directement à partir des pixels bruts : min/max/mean/stddev/median par canal, histogram, dimensions. Les champs HFRavg, HFRavgDev et starsCount existent bien dans la structure, mais ImgStats() les laisse à zéro.
Toute analyse d’image plus poussée (détection d’étoiles, HFR, plate solving…) est un sujet métier, propre à chaque module, qui n’entre pas dans le périmètre de ce framework — ce n’est pas le rôle de cette documentation de le couvrir. Pour s’inspirer d’une implémentation réelle, voir par exemple le module Focus du core (src/modules/focus/focus.cpp), qui gère ça via sa propre instance de la classe Solver (libs/solver.cpp).