web-dev-qa-db-fra.com

Comment utiliser la fonction de remappage d'OpenCV?

Voici le cas de test le plus simple possible pour remap ():

import cv2
import numpy as np
inimg = np.arange(2*2).reshape(2,2).astype(np.float32)
inmap = np.array([[0,0],[0,1],[1,0],[1,1]]).astype(np.float32)
outmap = np.array([[10,10],[10,20],[20,10],[20,20]]).astype(np.float32)
outimg = cv2.remap(inimg,inmap,outmap,cv2.INTER_LINEAR)
print "inimg:",inimg
print "inmap:",inmap
print "outmap:",outmap
print "outimg:", outimg

et voici la sortie:

inimg: [[ 0.  1.]
 [ 2.  3.]]
inmap: [[ 0.  0.]
 [ 0.  1.]
 [ 1.  0.]
 [ 1.  1.]]
outmap: [[ 10.  10.]
 [ 10.  20.]
 [ 20.  10.]
 [ 20.  20.]]
outimg: [[ 0.  0.]
 [ 0.  0.]
 [ 0.  0.]
 [ 0.  0.]]

Comme vous pouvez le voir, outimg produit 0,0, et il n'est même pas dans la bonne forme. Je m'attends à une image 20x20 ou 10x10 avec des valeurs interpolées de la plage 0 à 3.

J'ai lu toute la documentation. Il et tout le monde sur SO indique que vous entrez un tableau (une carte) de points de départ, une carte de points de fin, puis remap () mettra toutes les valeurs img dans leurs nouvelles positions, interpoler tout espace vide. Je le fais, mais cela ne fonctionne tout simplement pas. Pourquoi? La plupart des exemples sont pour C++. Est-il cassé en python?

11
john ktejik

Ce n'est qu'un simple malentendu de la documentation, et je ne vous en veux pas --- il m'a fallu quelques tâtonnements pour le comprendre aussi. Les documents sont clairs, mais cette fonction ne fonctionne probablement pas de la manière attendue; en fait, cela fonctionne dans le sens opposé par rapport à ce que j'attendais au départ.

Ce que remap() ne fait pas est de prendre les coordonnées de votre image source, de transformer les points, puis d'interpoler. Ce que remap() fait fait, pour chaque pixel de l'image destination, recherche d'où il vient dans l'image source, puis attribue une valeur interpolée. Il doit fonctionner de cette façon car, pour interpoler, il doit regarder les valeurs autour de l'image source à chaque pixel. Permettez-moi de développer (peut-être me répéter un peu, mais ne le prenez pas dans le mauvais sens).

Depuis les documents remap() :

map1 - La première carte des points (x,y) ou simplement des valeurs x ayant le type CV_16SC2 , CV_32FC1 Ou CV_32FC2. Voir convertMaps() pour plus de détails sur la conversion d'une représentation à virgule flottante en virgule fixe pour la vitesse.

map2 - La deuxième carte de valeurs y ayant le type CV_16UC1, CV_32FC1, ou aucune (carte vide si map1 vaut (x,y) points), respectivement.

Le verbage ici sur map1 Avec "la carte d'abord de ..." est quelque peu trompeur. Rappelez-vous, ce sont strictement les coordonnées de l'endroit où votre image est mappée de ... les points sont mappés desrc à map_x(x, y), map_y(x, y) puis placé dans dst à x, y. Et ils devraient avoir la même forme que l'image que vous souhaitez déformer à. Notez l'équation montrée dans les documents:

dst(x,y) =  src(map_x(x,y),map_y(x,y))

Ici, map_x(x, y) recherche map_x Dans les lignes et colonnes données par x, y. Ensuite, l'image est évaluée à ces points. Il recherche les coordonnées mappées de x, y Dans src, puis affecte cette valeur à x, y Dans dst. Si vous le regardez assez longtemps, cela commence à avoir un sens. Au pixel (0, 0) Dans la nouvelle image de destination, je regarde map_x Et map_y Qui me disent l'emplacement du pixel correspondant dans l'image source, puis je peux attribuer un valeur interpolée à (0, 0) dans l'image de destination en regardant les valeurs proches dans la source. C'est en quelque sorte la raison fondamentale pour laquelle remap() fonctionne de cette façon; il a besoin de savoir d'où vient un pixel pour pouvoir voir les pixels voisins à interpoler.

Petit exemple artificiel

img = np.uint8(np.random.Rand(8, 8)*255)
#array([[230,  45, 153, 233, 172, 153,  46,  29],
#       [172, 209, 186,  30, 197,  30, 251, 200],
#       [175, 253, 207,  71, 252,  60, 155, 124],
#       [114, 154, 121, 153, 159, 224, 146,  61],
#       [  6, 251, 253, 123, 200, 230,  36,  85],
#       [ 10, 215,  38,   5, 119,  87,   8, 249],
#       [  2,   2, 242, 119, 114,  98, 182, 219],
#       [168,  91, 224,  73, 159,  55, 254, 214]], dtype=uint8)

map_y = np.array([[0, 1], [2, 3]], dtype=np.float32)
map_x = np.array([[5, 6], [7, 10]], dtype=np.float32)
mapped_img = cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)
#array([[153, 251],
#       [124,   0]], dtype=uint8)

Alors qu'est-ce qui se passe ici? N'oubliez pas que ce sont les indices de img qui seront mappés sur la ligne et la colonne où ils se trouvent. Dans ce cas, il est plus facile d'examiner les matrices:

map_y
=====
0  1
2  3

map_x
=====
5  6
7  10

Ainsi, l'image de destination à (0, 0) a la même valeur que l'image source à map_y(0, 0), map_x(0, 0) = 0, 5 et l'image source à la ligne 0 et la colonne 5 est 153. Notez que dans l'image de destination mapped_img[0, 0] = 153. Aucune interpolation ne se produit ici car mes coordonnées cartographiques sont des entiers exacts. J'ai également inclus un index hors limites (map_x[1, 1] = 10, Qui est plus grand que la largeur de l'image), et je remarque qu'il obtient simplement la valeur 0 Lorsqu'il est hors limites.

Exemple de cas d'utilisation complet

Voici un exemple de code à part entière, utilisant une homographie de vérité au sol, déformant manuellement les emplacements des pixels et utilisant remap() pour ensuite mapper l'image à partir des points transformés. Notez ici que mon homographie transforme true_dst ensrc. Ainsi, je fais un ensemble de autant de points que je veux, puis calcule où ces points se trouvent dans l'image source en transformant avec l'homographie. Ensuite, remap() est utilisée pour rechercher ces points dans l'image source et les mapper dans l'image de destination.

import numpy as np
import cv2

# read images
true_dst = cv2.imread("img1.png")
src = cv2.imread("img2.png")

# ground truth homography from true_dst to src
H = np.array([
    [8.7976964e-01,   3.1245438e-01,  -3.9430589e+01],
    [-1.8389418e-01,   9.3847198e-01,   1.5315784e+02],
    [1.9641425e-04,  -1.6015275e-05,   1.0000000e+00]])

# create indices of the destination image and linearize them
h, w = true_dst.shape[:2]
indy, indx = np.indices((h, w), dtype=np.float32)
lin_homg_ind = np.array([indx.ravel(), indy.ravel(), np.ones_like(indx).ravel()])

# warp the coordinates of src to those of true_dst
map_ind = H.dot(lin_homg_ind)
map_x, map_y = map_ind[:-1]/map_ind[-1]  # ensure homogeneity
map_x = map_x.reshape(h, w).astype(np.float32)
map_y = map_y.reshape(h, w).astype(np.float32)

# remap!
dst = cv2.remap(src, map_x, map_y, cv2.INTER_LINEAR)
blended = cv2.addWeighted(true_dst, 0.5, dst, 0.5, 0)
cv2.imshow('blended.png', blended)
cv2.waitKey()

Remap for warping

Images et homographies de la vérité du terrain du Visual Geometry Group à Oxford .

37
alkasm