c++ - kmeans - k means image segmentation python
OpenCV usando k-means para posterizar una imagen (2)
No soy un experto en OpenCV, por lo que le daré un consejo general que se relaciona con su pregunta K-means toma una lista de vectores que es esencialmente una matriz:
[x0, y0, r0, g0, b0]
[x1, y1, r1, g1, b1]
[x2, y2, r2, g2, b2]
.
.
.
Le estás dando una imagen que no va a funcionar. Primero debes convertir la imagen a este formato de matriz k-means. Para cada píxel de la imagen de origen, tiene una fila en la matriz resultante. También tenga en cuenta que debe escalar los valores para que todos tengan valores similares. Si no lo hace, las coordenadas x e y generalmente tendrán una "gravedad" mucho más alta que el color que conduce a resultados insatisfactorios. Cseudocódigo C ++:
int pixel_index = 0;
for (int y = 0; y < image height; y++) {
for (int x = 0; x < image width; x++) {
matrix[pixel_index][0] = (float)x / image width;
matrix[pixel_index][1] = (float)y / image height;
matrix[pixel_index][2] = (float)pixel(x, y).r / 255.0f;
matrix[pixel_index][3] = (float)pixel(x, y).g / 255.0f;
matrix[pixel_index][4] = (float)pixel(x, y).b / 255.0f;
}
}
// Pass the matrix to kmeans...
Como resultado, obtiene etiquetas de cada píxel individual que corresponde al clúster al que se le ha asignado. Luego, debe determinar el color de los grupos: esto puede variar desde tomar el valor del color del píxel central hasta calcular el color promedio / medio del grupo. Después de determinar el color, simplemente recorra la imagen y establezca los píxeles en los colores de su grupo:
for (int y = 0; y < image height; y++) {
for (int x = 0; x < image width; x++) {
int index = y * image width + x; // This corresponds to pixel_index above
int cluster_index = labels[index]; // 0 to 7 in your case
Color color = colors[cluster_index]; // Colors is an array of 8 colors of the clusters
image.setpixel(x, y, color)
}
}
Si prefiere utilizar HSV en lugar de RGB, solo use valores de HSV en lugar de RGB.
Es posible que OpenCV tenga funciones que realicen exactamente la conversión que describí anteriormente, pero no pude encontrarlas rápidamente usando Google.
Quiero posterizar una imagen con k-means y OpenCV en la interfaz C ++ (cv namespace) y obtengo resultados extraños. Lo necesito para reducir algo de ruido. Este es mi código:
#include "cv.h"
#include "highgui.h"
using namespace cv;
int main() {
Mat imageBGR, imageHSV, planeH, planeS, planeV;
imageBGR = imread("fruits.jpg");
imshow("original", imageBGR);
cv::Mat labels, data;
cv::Mat centers(8, 1, CV_32FC1);
imageBGR.convertTo(data, CV_32F);
cv::kmeans(data, 8, labels,
cv::TermCriteria(CV_TERMCRIT_ITER, 10, 1.0),
3, cv::KMEANS_PP_CENTERS, ¢ers);
imshow("posterized hue", data);
data.convertTo(data, CV_32FC3);
waitKey();
return 0;
}
Pero me sale un resultado raro.
Primera imagen: original
Segunda imagen: después de k-medias.
¿Algún consejo?
Actualización: la solución correcta. Tal vez alguien me puede ayudar a optimizar el código?
#include "cv.h"
#include "highgui.h"
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat src;
src = imread("fruits.jpg");
imshow("original", src);
blur(src, src, Size(15,15));
imshow("blurred", src);
Mat p = Mat::zeros(src.cols*src.rows, 5, CV_32F);
Mat bestLabels, centers, clustered;
vector<Mat> bgr;
cv::split(src, bgr);
// i think there is a better way to split pixel bgr color
for(int i=0; i<src.cols*src.rows; i++) {
p.at<float>(i,0) = (i/src.cols) / src.rows;
p.at<float>(i,1) = (i%src.cols) / src.cols;
p.at<float>(i,2) = bgr[0].data[i] / 255.0;
p.at<float>(i,3) = bgr[1].data[i] / 255.0;
p.at<float>(i,4) = bgr[2].data[i] / 255.0;
}
int K = 8;
cv::kmeans(p, K, bestLabels,
TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0),
3, KMEANS_PP_CENTERS, centers);
int colors[K];
for(int i=0; i<K; i++) {
colors[i] = 255/(i+1);
}
// i think there is a better way to do this mayebe some Mat::reshape?
clustered = Mat(src.rows, src.cols, CV_32F);
for(int i=0; i<src.cols*src.rows; i++) {
clustered.at<float>(i/src.cols, i%src.cols) = (float)(colors[bestLabels.at<int>(0,i)]);
// cout << bestLabels.at<int>(0,i) << " " <<
// colors[bestLabels.at<int>(0,i)] << " " <<
// clustered.at<float>(i/src.cols, i%src.cols) << " " <<
// endl;
}
clustered.convertTo(clustered, CV_8U);
imshow("clustered", clustered);
waitKey();
return 0;
}
Resultado:
Si no necesita las coordenadas x, y en sus k-medias, puede organizar los datos mucho más rápido de la siguiente manera utilizando el comando remodelar:
int origRows = img.rows;
notes << "original image is: " << img.rows << "x" << img.cols << endl;
Mat colVec = img.reshape(1, img.rows*img.cols); // change to a Nx3 column vector
cout << "colVec is of size: " << colVec.rows << "x" << colVec.cols << endl;
Mat colVecD, bestLabels, centers, clustered;
int attempts = 5;
int clusts = 8;
double eps = 0.001;
colVec.convertTo(colVecD, CV_32FC3, 1.0/255.0); // convert to floating point
double compactness = kmeans(colVecD, clusts, bestLabels,
TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, attempts, eps),
attempts, KMEANS_PP_CENTERS, centers);
Mat labelsImg = bestLabels.reshape(1, origRows); // single channel image of labels
cout << "Compactness = " << compactness << endl;