2012-04-20 23 views
5

Esta pregunta es específica de opencv: El ejemplo de kmeans dado en la documentación de opencv tiene una matriz de 2 canales, un canal para cada dimensión del vector de características. Pero, algunos de los otros ejemplos parecen decir que debe ser una matriz de un canal con características a lo largo de las columnas con una fila para cada muestra. ¿Cuál de estos es correcto?Matriz de entrada a opencv kmeans clustering

si tengo un vector 5 característica dimensional, lo que debería ser la matriz de entrada que utilizo: Éste:

cv::Mat inputSamples(numSamples, 1, CV32FC(numFeatures)) 

o ésta:

cv::Mat inputSamples(numSamples, numFeatures, CV_32F) 

Respuesta

28

La respuesta correcta es cv::Mat inputSamples(numSamples, numFeatures, CV_32F) . El OpenCV Documentación sobre kmeanssays:

muestras - matriz de punto flotante de muestras de entrada, una fila por muestra

Por lo tanto, no es un vector de punto flotante de los flotadores n-dimensional como en la otra opción. ¿Qué ejemplos sugirieron tal comportamiento?

Aquí también hay un pequeño ejemplo que muestra cómo se pueden usar kmeans. Se agrupa los píxeles de una imagen y muestra el resultado:

#include "opencv2/imgproc/imgproc.hpp" 
#include "opencv2/highgui/highgui.hpp" 

using namespace cv; 

int main(int argc, char** argv) 
{ 
    Mat src = imread(argv[1], 1); 
    Mat samples(src.rows * src.cols, 3, CV_32F); 
    for(int y = 0; y < src.rows; y++) 
    for(int x = 0; x < src.cols; x++) 
     for(int z = 0; z < 3; z++) 
     samples.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y,x)[z]; 


    int clusterCount = 15; 
    Mat labels; 
    int attempts = 5; 
    Mat centers; 
    kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers); 


    Mat new_image(src.size(), src.type()); 
    for(int y = 0; y < src.rows; y++) 
    for(int x = 0; x < src.cols; x++) 
    { 
     int cluster_idx = labels.at<int>(y + x*src.rows,0); 
     new_image.at<Vec3b>(y,x)[0] = centers.at<float>(cluster_idx, 0); 
     new_image.at<Vec3b>(y,x)[1] = centers.at<float>(cluster_idx, 1); 
     new_image.at<Vec3b>(y,x)[2] = centers.at<float>(cluster_idx, 2); 
    } 
    imshow("clustered image", new_image); 
    waitKey(0); 
} 
+0

Me gustaría saber qué está haciendo en el ciclo antes de la declaración de la variable clusterCount y también lo que está haciendo al final en el para después de los kmeans. ¿Crees que es posible actualizar la respuesta con esta información? ¡Gracias! –

+0

El primer ciclo reordena los datos de la imagen desde una matriz (rows, cols, 3) a una matriz (rows * cols, 3) (una fila por píxel). El bucle al final reemplaza cada píxel en la imagen con el centro del clúster correspondiente para la visualización. – sietschie

+0

¿Es posible usar 'Mat :: reshape()' en lugar de anidados para bucles? – Jayesh

1

Como alternativa a la remodelación de la matriz de entrada manual, puede utilizar la función de OpenCV reshape para lograr resultado similar con menos código. Aquí está mi aplicación trabaja de Reducción del número de colores con el método de K-medias (en Java):

private final static int MAX_ITER = 10; 
private final static int CLUSTERS = 16; 

public static Mat colorMapKMeans(Mat img, int K, int maxIterations) { 

    Mat m = img.reshape(1, img.rows() * img.cols()); 
    m.convertTo(m, CvType.CV_32F); 

    Mat bestLabels = new Mat(m.rows(), 1, CvType.CV_8U); 
    Mat centroids = new Mat(K, 1, CvType.CV_32F); 
    Core.kmeans(m, K, bestLabels, 
       new TermCriteria(TermCriteria.COUNT | TermCriteria.EPS, maxIterations, 1E-5), 
       1, Core.KMEANS_RANDOM_CENTERS, centroids); 
    List<Integer> idx = new ArrayList<>(m.rows()); 
    Converters.Mat_to_vector_int(bestLabels, idx); 

    Mat imgMapped = new Mat(m.size(), m.type()); 
    for(int i = 0; i < idx.size(); i++) { 
     Mat row = imgMapped.row(i); 
     centroids.row(idx.get(i)).copyTo(row); 
    } 

    return imgMapped.reshape(3, img.rows()); 
} 

public static void main(String[] args) { 
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME); 
    Highgui.imwrite("result.png", 
     colorMapKMeans(Highgui.imread(args[0], Highgui.CV_LOAD_IMAGE_COLOR), 
      CLUSTERS, MAX_ITER)); 
} 

OpenCV lee la imagen en 2, matriz de 3 dimensiones del canal. Primera llamada al reshape - img.reshape(1, img.rows() * img.cols()); - esencialmente desenrolla 3 canales en columnas. En la matriz resultante, una fila corresponde a un píxel de la imagen de entrada, y 3 columnas corresponden a componentes RGB.

Después de K-medias algoritmo terminó su trabajo, y la asignación de colores se ha aplicado, que llamamos reshape nuevo - imgMapped.reshape(3, img.rows()), pero ahora rodando columnas de nuevo en los canales, y la reducción de los números de fila para el número de fila imagen original, consiguiendo de este modo recuperar el formato de matriz original, pero solo con colores reducidos.

+0

I cree que debe tener cuidado de que la imagen sea continua antes de tomar este enfoque http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat-iscontinuous – ejectamenta

+0

si usa clone, es decir. usando clone como en img.clone(). reshape (1, img.rows() * img.cols()) entonces la imagen será continua (y su imagen original no se modificará) – ejectamenta