¿Cómo uso la función de reasignación de OpenCV?

Aquí está el caso de prueba más simple posible para 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 

y aquí está la salida:

 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.]] 

Como puede ver, outimg produce 0,0, y ni siquiera tiene la forma correcta. Espero una imagen de 20×20 o 10×10 con valores interpolados de rango 0 a 3.

He leído toda la documentación. Todo el mundo en SO declara que ingresas una matriz (un mapa) de puntos de inicio, un mapa de puntos finales y luego remap () colocará todos los valores de img en sus nuevas posiciones, interpolando cualquier espacio vacío. Estoy haciendo eso, pero simplemente no funciona. ¿Por qué? La mayoría de los ejemplos son para C ++. ¿Está roto en python?

    Esto es solo un simple malentendido de la documentación, y no te culpo, también me costó entender algunas cosas. Los documentos son claros, pero esta función probablemente no funcione de la manera que usted espera; de hecho, funciona en la dirección opuesta a la que esperaba al principio.

    Lo que remap() no hace es tomar las coordenadas de su imagen de origen, transformar los puntos y luego interpolar. Lo que remap() hace es, para cada píxel en la imagen de destino , buscar de dónde viene en la imagen de origen, y luego asigna un valor interpolado. Debe funcionar de esta manera ya que, para interpolar, debe mirar los valores alrededor de la imagen de origen en cada píxel. Déjame expandir (podría repetirme un poco, pero no lo tomes a mal).

    Desde los documentos remap() :

    map1 : el primer mapa de puntos (x,y) o simplemente valores x que tienen el tipo CV_16SC2 , CV_32FC1 o CV_32FC2 . Consulte convertMaps() para obtener detalles sobre la conversión de una representación de punto flotante a punto fijo para la velocidad.

    map2 : el segundo mapa de valores y que tiene el tipo CV_16UC1 , CV_32FC1 o ninguno (el mapa vacío si map1 es (x,y) puntos), respectivamente.

    El verbo aquí en map1 con “el primer mapa de …” es un tanto engañoso. Recuerde, estas son estrictamente las coordenadas de donde se asigna su imagen desde … los puntos se asignan desde src en map_x(x, y), map_y(x, y) y luego se colocan en dst en x, y . Y deben tener la misma forma de la imagen a la que se quiere deformar. Note la ecuación mostrada en los documentos:

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

    Aquí map_x(x, y) busca map_x en las filas y columnas dadas por x, y . Entonces la imagen es evaluada en esos puntos. Está buscando las coordenadas mapeadas de x, y en src , y luego asignando ese valor a x, y en dst . Si miras esto lo suficiente, comienza a tener sentido. En el píxel (0, 0) en la nueva imagen de destino, veo map_x y map_y que me indican la ubicación del píxel correspondiente en la imagen de origen, y luego puedo asignar un valor interpolado a (0, 0) en el destino Imagen mirando valores cercanos en la fuente. Esta es una de las razones fundamentales por las que remap() funciona de esta manera; necesita saber de dónde proviene un píxel para poder ver los píxeles adyacentes para interpolar.

    Ejemplo pequeño, ideado

     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) 

    Entonces, ¿qué está pasando aquí? Recuerde que estos son los índices de img que se asignarán a la fila y columna en la que se encuentran. En este caso, es más fácil examinar las matrices:

     map_y ===== 0 1 2 3 map_x ===== 5 6 7 10 

    Por lo tanto, la imagen de destino en (0, 0) tiene el mismo valor que la imagen de origen en map_y(0, 0), map_x(0, 0) = 0, 5 y la imagen de origen en la fila 0 y la columna 5 es 153. Nota que en la imagen de destino mapped_img[0, 0] = 153 . Aquí no se produce ninguna interpolación, ya que las coordenadas de mi mapa son enteros exactos. También map_x[1, 1] = 10 índice de fuera de límites ( map_x[1, 1] = 10 , que es más grande que el ancho de la imagen), y noté que solo se le asigna el valor 0 cuando está fuera de límites.

    Ejemplo completo de caso de uso

    Aquí hay un ejemplo de código completo, que utiliza una homografía de la verdad fundamental, distorsiona las ubicaciones de píxeles manualmente y usa remap() para luego mapear la imagen desde los puntos transformados. Tenga en cuenta que mi homografía transforma true_dst en src . Por lo tanto, hago un conjunto de tantos puntos como quiera, y luego calculo dónde se encuentran esos puntos en la imagen de origen mediante la transformación con la homografía. Luego, remap() se usa para buscar esos puntos en la imagen de origen y asignarlos a la imagen de destino.

     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 para la deformación

    Imágenes y homografías de la verdad de fondo del Grupo de Geometría Visual en Oxford .