5.9. Opérations arithmétiques¶
La famille de dessin de la section précédente peint dans une image. La famille arithmétique combine deux images en une troisième – en additionnant leurs valeurs de pixels, en soustrayant l’une de l’autre, en prenant le minimum ou le maximum à chaque position. Ce petit ensemble d’opérations arithmétiques pixel par pixel est le fondement du calcul de différence entre trames, de la soustraction d’arrière-plan, de l’empilement d’expositions et de quelques autres motifs classiques.
La famille arithmétique de la classe Image est suffisamment réduite pour être énumérée d’un seul coup :
add()–self + otherpar pixel, écrêté au maximum du format.sub()–self - otherpar pixel, écrêté à0par le bas.rsub()–other - selfpar pixel, écrêté à0(la même arithmétique quesubavec les opérandes inversés).min()– minimum par pixel des deux valeurs.max()– maximum par pixel.difference()–|self - other|par pixel, la différence absolue.
Plus deux opérations connexes sur une seule image :
invert()– remplace chaque pixel par255 - pixel(ou le maximum équivalent pour le format).
Deux dégradés sources A et B, et le résultat de chaque opération par paires qui leur est appliquée. Chaque opération s’exécute position par position – ce qui apparaît dans le résultat à un emplacement donné ne dépend que des deux pixels sources à cet emplacement.¶
5.9.1. Deux formes d’opérande¶
Chacune des méthodes à deux images accepte l’une ou l’autre forme pour son second opérande :
Une autre
Imagede mêmes dimensions. L’arithmétique s’exécute position par position – le résultat en(x, y)est l’opération appliquée aux pixels sources en(x, y)des deux images.Une valeur scalaire – un entier pour les niveaux de gris, un tuple
(r, g, b)pour le RGB565. Le même scalaire s’applique à chaque position.
La forme scalaire est utile lorsque l’application souhaite décaler chaque pixel d’une quantité constante. img.add(40) éclaircit toute l’image de 40 ; img.sub((20, 20, 20)) assombrit chaque pixel de 20 par canal ; img.max(50) relève tout pixel inférieur à 50 jusqu’à 50 et laisse le reste inchangé – le genre d’opération qui transforme un plancher de capteur quasi noir en un gris foncé uniforme contre lequel les étapes suivantes peuvent travailler.
5.9.2. Écrêtage¶
Les valeurs des pixels restent à l’intérieur de la plage du format à chaque opération. Pour un canal 8 bits, cela signifie de 0 à 255 : tout ce qui aurait dépassé 255 est ramené à 255, et tout ce qui serait passé en dessous de 0 est remonté à 0. Il n’y a pas de bouclage.
Ce choix a son importance en pratique. add, qui éclaircit les pixels, ne produit jamais d’artefact d’assombrissement soudain à l’extrémité claire là où le calcul déborderait autrement ; sub, qui assombrit les pixels, ne produit jamais d’artefact d’éclaircissement soudain à l’extrémité sombre là où il subirait autrement un dépassement par le bas. Les résultats restent visuellement cohérents au prix d’une certaine perte d’information aux extrêmes saturés.
L’écrêtage explique aussi pourquoi sub et rsub renvoient des résultats différents l’un de l’autre. img_a.sub(img_b) donne la partie de a qui est plus claire que b et zéro partout ailleurs ; img_a.rsub(img_b) donne la partie de b qui est plus claire que a. L’une comme l’autre est utile pour la détection de changement unilatérale – si l’application ne s’intéresse qu’aux pixels qui se sont éclaircis, ou seulement à ceux qui se sont assombris – mais aucune ne capture tous les changements entre deux trames.
5.9.3. L’opération de différence¶
Pour la détection de changement bilatérale, l’opération à privilégier est difference(), qui calcule |self - other| à chaque position – la différence absolue, sans signe. Chaque pixel qui a changé dans un sens ou dans l’autre apparaît comme une valeur non nulle dans le résultat, avec une amplitude proportionnelle à l’ampleur de son changement à cette position.
Cette propriété – non nulle exactement là où les deux images diffèrent – est ce qui fait de difference la cheville ouvrière de la détection de changement trame par trame. Une trame de référence stockée au démarrage et une nouvelle capture, passées par difference, produisent une image dont les pixels non nuls marquent chaque position où quelque chose dans la scène a bougé ou changé de luminosité.
5.9.4. Cadrage avec un masque¶
Toutes les méthodes arithmétiques acceptent l’argument nommé mask présenté sur la page des régions et masques. Lorsqu’un masque est passé, l’opération ne s’exécute qu’aux positions où le masque est non nul ; partout ailleurs, l’image de destination est laissée inchangée.
Cette composition apparaît dans deux motifs. Le premier consiste à contraindre une opération à une zone connue : additionner deux trames uniquement à l’intérieur de la boîte englobante d’un marqueur détecté, par exemple. Le second consiste à construire une trame composite morceau par morceau – min sur une séquence de trames à l’intérieur d’un masque de premier plan, max sur la même séquence à l’intérieur du masque complémentaire – ce genre de motif.
5.9.5. Sur place, et préservation des entrées¶
Les méthodes arithmétiques suivent toutes la convention de fonctionnement établie précédemment : chacune modifie l’image source sur place et renvoie la même image pour le chaînage. Les pixels de la source ont disparu après l’appel – remplacés par le résultat de l’opération appliquée à ce qui a été passé comme second opérande.
Lorsque l’application a besoin de préserver les deux entrées, le motif sûr consiste à copier d’abord l’une d’elles :
diff = current.copy() # leaves current intact
diff.difference(reference) # diff now holds the absolute difference
Ce motif – copier, puis opérer – est l’épine dorsale de tout pipeline de calcul de différence entre trames, où la trame de référence doit survivre à la comparaison pour pouvoir être réutilisée sur la trame capturée suivante.
Avec six opérations de combinaison, deux opérations sur une seule image, une cheville ouvrière de différence absolue et l’argument nommé mask pour le cadrage, la boîte à outils d’arithmétique de pixels couvre les combinaisons de luminosité et de canaux dont la vision industrielle classique a besoin. Les outils restants, apparentés à l’arithmétique en apparence, travaillent bit par bit plutôt que valeur par valeur.