为什么Mat.forEach不会改变自己?

时间:2016-01-21 10:24:18

标签: c++ algorithm opencv

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

步骤如下:

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

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

  3. 来源是:

    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()中的断言()不会被激发。也就是说,mat对象 original_img output_img 相同。怎么会发生这种情况?

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

    3. 有缺陷的输出如下:

      The buggy output

      K = 16的原始图像和K-Means可以在下面看到:

      The original image

      K-Means with K=16

1 个答案:

答案 0 :(得分:1)

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

在kmeans()中,下面的代码将调用Mat的复制构造函数,它需要花费O(1)将original_img的头部分配给output_img。

Mat output_img = original_img;

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