OpenCV с использованием k-средств для вставки изображения
Я хочу загрузить изображение с k-средствами и OpenCV в С++-интерфейсе (пространство имен cv), и я получаю странные результаты. Мне нужно это для уменьшения шума. Это мой код:
#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;
}
Но я получаю странный результат
![Fruit]()
Первое изображение: оригинал
Второе изображение: после k-средних.
Любые советы?
Обновление: правильное решение. может кто-то может помочь мне в оптимизации кода?
#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;
}
Результат:
![Posterized Fruit]()
Ответы
Ответ 1
Я не эксперт в OpenCV, поэтому я дам общий совет, который относится к вашему вопросу. K-mean принимает список векторов, который по существу является матрицей:
[x0, y0, r0, g0, b0]
[x1, y1, r1, g1, b1]
[x2, y2, r2, g2, b2]
.
.
.
Вы даете ему изображение, которое не будет работать. Сначала вам нужно преобразовать изображение в этот матричный формат k-mean. Для каждого пикселя исходного изображения у вас есть одна строка в результирующей матрице. Также обратите внимание, что вы должны масштабировать значения, чтобы все они имели одинаковые значения. Если вы этого не сделаете, координаты x и y, как правило, будут иметь гораздо более высокую "гравитацию", чем цвет, что приводит к неудовлетворительным результатам. С++ псевдокод:
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...
В результате вы получаете метки каждого отдельного пикселя, который соответствует клану, которому он был назначен. Затем вам необходимо определить цвет кластеров - это может варьироваться от принятия значения центрального пикселя к вычислению среднего/медианного цвета кластера. После того, как вы определите цвет, просто пройдите изображение и установите пиксели в цвета их кластера:
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)
}
}
Если вы предпочитаете использовать HSV вместо RGB, просто используйте значения HSV вместо RGB.
Возможно, что в OpenCV есть функции, которые выполняют именно описанное выше преобразование, но я не смог быстро найти их с помощью Google.
Ответ 2
Если вам не нужны координаты x, y в ваших k-значках, вы можете упорядочить данные намного быстрее, как описано ниже, используя команду reshape:
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;