【问题标题】:Why does Mat.forEach doesn't change itself?为什么 Mat.forEach 不会改变自己?
【发布时间】:2016-01-21 10:24:18
【问题描述】:

基本上,我正在尝试使用更少的像素来表示图像本身。

步骤如下:

  1. 假设我将输入一个大小为 [1000*600] 的图像,然后我得到 600_000 像素(rgb),这可能是 [600_000, 3] 个向量。 K-Means 用于获取其聚类中心。

  2. 图像中的每个像素都将与其最近邻放置在通过 K-Means 找到的聚类中。

来源是:

template <typename T>
void NN(Point3_<T>& pixel, const Mat& points)
{
    vector<T> vt {pixel.x, pixel.x, pixel.z};
    double min_dist = LDBL_MAX;
    int min_index = -1;
    for (int i = 0; i < points.rows; ++ i)
    {
        double dist = norm(vt, points.row(i), NORM_L2);
        if (dist < min_dist) 
        {
            min_dist = dist;
            min_index = i;
        }
    }
    // assert(min_index != -1);
    pixel.x = points.at<T>(min_index, 0);
    pixel.y = points.at<T>(min_index, 1);
    pixel.z = points.at<T>(min_index, 2);
}

template <typename T>
void NN(Mat& img, const Mat& points)
{
    timer::start("assign");
    img.forEach<Point3_<T>>([&points](Point3_<T> &pixel, const int position[])
        {
            NN(pixel, points);
        });
    timer::stop<ms>();
}

Mat kmeans(const Mat& original_img, const int K)
{
    Mat img;
    original_img.reshape(3, original_img.rows * original_img.cols)
        .convertTo(img, CV_32FC3);

    timer::start("K-means cluster");
    // Require img.type() == CV_32F
    Mat clusters = BOWKMeansTrainer(K).cluster(img);
    timer::stop<ms>();

    // Type 5 -> Type 0: 32FC1 -> 8UC1
    // K rows, 3 cols, 8UC1
    clusters.convertTo(clusters, CV_8UC1);
    Mat output_img = original_img;
    NN<uchar>(output_img, clusters);

    // assert won't fire, why?
    assert(equal(original_img.begin<uchar>(), original_img.end<uchar>(),
        output_img.begin<uchar>()));

    return output_img;
}

int main(int argc, char* argv[])
{   
    vector<int> ks {2, 16};
    string filename = "1";
    string pathname = string("./img/") + filename + ".jpg";

    Mat img = imread(pathname);
    for (const int& K: ks)
    {
        imshow(int_to_string(K), kmeans(img, K));
        // write_img(filename, "kmeans", K, kmeans(img, K));
    }

    std::cout << "Press enter to continue...";
    cin.get();
}

问题是:

  1. kmeans() 中的 assert() 不会触发。也就是说,mat 对象 original_imgoutput_img 相同。怎么会这样?

  2. main() 中的两个 imwrite() 将显示两个相同的 2 值图像。也就是说,K=2 的 K-Means 有效,而以下 K=16 无效。请注意,如果我们每次执行输出一张图像,一切都很好。

错误输出如下:

K=16 的原始图像和 K-Means 如下所示:

【问题讨论】:

    标签: c++ algorithm opencv


    【解决方案1】:

    感谢上帝!我找到了原因。

    在 kmeans() 中,下面的代码会调用 Mat 的复制构造函数,将 original_img 的 header 分配给 output_img 的成本为 O(1)。

    Mat output_img = original_img;
    

    这就是断言不会触发的原因。

    【讨论】:

      猜你喜欢
      • 2018-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-29
      • 2016-05-27
      相关资源
      最近更新 更多