2011-01-20 9 views
6

estoy usando el siguiente código para agregar un poco de ruido a una imagen (directamente de la referencia OpenCV, página 449 - explicación de cv::Mat::begin):Determinación del tipo de plantilla cuando se accede a los elementos OpenCV Mat

void 
simulate_noise(Mat const &in, double stddev, Mat &out) 
{ 
    cv::Size s = in.size(); 
    vector<double> noise = generate_noise(s.width*s.height, stddev); 

    typedef cv::Vec<unsigned char, 3> V4; 
    cv::MatConstIterator_<V4> in_itr = in.begin<V4>(); 
    cv::MatConstIterator_<V4> in_end = in.end<V4>(); 
    cv::MatIterator_<V4> out_itr = out.begin<V4>(); 
    cv::MatIterator_<V4> out_end = out.end<V4>(); 

    for (; in_itr != in_end && out_itr != out_end; ++in_itr, ++out_itr) 
    { 
     int noise_index = my_rand(noise.size()); 
     for (int j = 0; j < 3; ++j) 
      (*out_itr)[j] = (*in_itr)[j] + noise[noise_index]; 
    } 
} 

Nada excesivamente complicado:

  • in y out se asignan cv::Mat objetos de las mismas dimensiones y el tipo
  • iterate la imagen de entradasobre
  • en cada posición, seleccione un valor aleatorio de noise (my_rand(int n) devuelve un número aleatorio en [0..n-1]
  • suma el píxel de in con el valor de ruido aleatorio
  • puso dan como resultado la suma en out

No me gusta este código porque la siguiente declaración parece inevitable:

typedef cv::Vec<unsigned char, 3> V4; 

Tiene codificadas de forma rígida dos cosas:

  1. Las imágenes tienen 3 canales
  2. La profundidad de canal se 8bpp

Si consigo este typedef mal (por ejemplo, profundidad de canal incorrecta o número incorrecto de canales), entonces mi programa segmenta. Originalmente utilicé typedef cv::Vec<unsigned char, 4> V4 para manejar imágenes con un número arbitrario de canales (el máximo que admite OpenCV es 4), pero esto provocó una segfault.

¿Hay alguna manera de evitar las dos cosas anteriores? Idealmente, quiero algo que sea tan genérico como:

typedef cv::Vec<in.type(), in.size()> V4; 

Respuesta

2

El problema es que debe determinar determinar el tipo y la cantidad de canales en tiempo de ejecución, pero las plantillas necesitan la información en tiempo de compilación. Puede evitar codificando el número de canales, ya sea usando cv::split y cv::merge, o cambiando la iteración a

for(int row = 0; row < in.rows; ++row) { 
    unsigned char* inp = in.ptr<unsigned char>(row); 
    unsigned char* outp = out.ptr<unsigned char>(row); 
    for (int col = 0; col < in.cols; ++col) { 
     for (int c = 0; c < in.channels(); ++c) { 
      *outp++ = *inp++ + noise(); 
     } 
    } 
} 

Si desea deshacerse de la dependencia del tipo, sugeriría poner lo anterior en una función de plantilla y llamando a eso desde su función, dependiendo del tipo de la matriz.

+0

Gracias por la respuesta. Puede que termine usando punteros de fila como usted mencionó, pero se dice un poco que los iteradores van porque este es el lugar perfecto para usarlos. Además, incluso si configuro la función, aún tengo que especificar su nombre de tipo en el momento de la compilación, que realmente solo mueve el problema a otro punto del código. Tal vez estoy siendo un poco cínico, pero a mí me parece que especificar esta información en tiempo de compilación derrota el punto de tener plantillas y polimorfismo en primer lugar. – misha

+0

No "solo" mueve el problema: si se fue sin la función de plantilla, tendría que replicar todo el código para el tipo. Además, el problema de fondo es que usted está tratando de operar en una imagen sin saber lo que contiene, y que está condenado al fracaso (por ejemplo, necesita diferentes ruido para imágenes de valores enteros que para las imágenes con valores reales. En el método Si vuelve generate_noise ruido gaussiano con media 0, el ruido tendrá una tendencia a oscurecer la imagen (debido enteros redondas _down_). – etarion

+0

Y no, tener que especificar esta información en tiempo de compilación no derrotar el punto de tener plantillas , que se hizo para esta cosa exacta - y el polimorfismo es una cuestión completamente – etarion

1

Están codificados porque el rendimiento es mejor así.

En OpenCV1.x hay cvGet2D(), que se puede usar aquí ya que Mat se puede convertir como IplImage. Pero es lento ya que cada vez que accede a un píxel la función descubrirá el tipo, tamaño, etc. Bucles in especialmente ineficientes.

+1

Gracias por la sugerencia, pero yo preferiría quedarse con la interfaz de C++, ya que es mucho más limpio (aparte de este problema, estoy muy contento con él). Además, no entiendo por qué 'cv :: Mat' no es una clase de plantilla, eso evitaría tener que moldear los iteradores/funciones de acceso. ¿Eso es por razones de eficiencia también? – misha

3

Sé que esto llega tarde. Sin embargo, la solución real a su problema es usar la funcionalidad OpenCV para hacer lo que quiera hacer.

  1. crear vector de ruido como que ya lo hacen (o utilizar las funciones que proporciona OpenCV pista!)
  2. vector de ruido aleatorio de modo que no es necesario noise_index individual para cada píxel; o crear el vector de ruido aleatorio de antemano
  3. construir una cabecera de matriz alrededor de su vector/aleatoria barajado: operaciones con matrices cv::Mat_<double>(noise);
  4. uso para el cálculo: out = in + noise; o cv::add(in, noise, out);
  5. beneficio!

Otra ventaja de este método es que OpenCV puede emplear multiprocesamiento, SSE o lo que sea para acelerar esta operación de elementos masivos, cosa que no hace. Su código es más simple, más limpio, y OpenCV hace todo el manejo de tipo desagradable para usted.

+0

Gracias, ** ** suena como una mejor manera. – misha

Cuestiones relacionadas