0

I am having an issue when I want to combine two computer graphic libraries, namely OpenCV and Vigra. I want to use OpenCVs k-means clustering algorithm for grayscale image binarization. The framework of my image processing was built earlier and depends strongly on Vigra, that's why I have to combine both libraries.

So basically, I am loading the image employing Vigra functionality, than convert the Vigra object to an OpenCV matrix, run the k-means clustering, re-convert the matrix object to a vigra object and finally save the image again by employing Vigra functionality. Here is a code example:

#include <vigra/impex.hxx>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>

int main()
{
    
    std::string InputFilePath = "path/to/image/image_name.tif";
    
    // Vigra functionality to load an image from path
    vigra::FImage InputImg;
    const char* cFile = InputFilePath.c_str();
    vigra::ImageImportInfo info(cFile);                 
    int b = info.width();
    int h = info.height();
    InputImg.resize(b,h);
    vigra::importImage(info, destImage(InputImg));

    vigra::FImage OutputImg(InputImg.width(), InputImg.height());

    // Setting up OCV Matrix as an one channel, 32bit float grayscale image
    cv::Mat InputMat(InputImg.width(), InputImg.height(), CV_32FC1);

    // my workaround to convert vigra::FImage to cv::Mat
    for(unsigned int i=0; i<InputImg.width(); i++){
        for(unsigned int j=0; j<InputImg.height(); j++){
            InputMat.at<float>(j,i) = InputImg(i,j);
        }
    }

    // OCVs k-means clustering
    const unsigned int singleLineSize = InputMat.rows*InputMat.cols;
    const unsigned int k=2;

    cv::Mat data = InputMat.reshape(1, singleLineSize);
    std::vector<int> labels;
    data.convertTo(data, CV_32FC1);
    cv::Mat1f centers;

    cv::kmeans(data, k, labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.0), 2, cv::KMEANS_RANDOM_CENTERS, centers);

    for (unsigned int i = 0; i < singleLineSize; i++) {
        data.at<float>(i) = centers(labels[i]);
    }

    cv::Mat OutputMat = data.reshape(1, InputMat.rows);
    OutputMat.convertTo(OutputMat, CV_8UC1);

    // re-convert cv::Mat to vigra::FImage
    for(unsigned int i=0; i<InputImg.width(); i++){
        for(unsigned int j=0; j<InputImg.height(); j++){
             OutputImg(i,j) = OutputMat.at<float>(j,i);
        }
    }

    std::string SaveFileName = "path/to/save_location/save_img_name.tif";

    // vigra functionality to save the image
    const char* cFile = SaveFileName.c_str();
    vigra::ImageExportInfo exinfo(cFile);
    vigra::exportImage(srcImageRange(OutputImg), exinfo.setPixelType("FLOAT")); // pixel type could also be "UINT8"

    // for the sake of comparability
    std::string SaveFileNameOCV = "path/to/save_location/save_mat_name.tif";
    cv::imwrite(SaveFileNameOCV, OutputMat);

    return 0;
}

k-means clustering works fine, and when I save the cv::Mat directly with

cv::imwrite()

everything is good. But when I re-convert the cv::Mat to a vigra::FImage object and save it, the image is corrupted. It looks as if the object (in the image) is mirrored or duplicated four times, allthough image width and heigth stay the same. I attached the images (InputImg, OutputImg and OutputMat). Moreover, if I re-convert InputMat to OutputImg (after the k-means), and save this image, everything is fine (this image is also attached).

And finally, I do not understand why I have to switch the indices when converting from vigra::FImage to cv::Mat and vice versa:

InputMat.at<float>(j,i) = InputImg(i,j);

But if I don't, the resulting image is rotated.

Ok, so I am not quite sure if anybody uses Vigra AND OpenCV, I guess OpenCV is definitly more common than Vigra. But anyway, if anybody could help, this would be great.

BTW: I am running everything in Code::Blocks on OpenSuSE 15.1. Any library was installed via the official OpenSuSE repositories.

InputImg OutputImg OutputMat InputMat_after_kmeans

Phtagen
  • 121
  • 10

1 Answers1

0

OK, first of all, I did not found out, why this happens. But what I now know, is that, if I am using the type-defined version Mat_ (e.g. Mat1f...) I can handle everything propperly and the saved results are as expected.

For conversion I wrote 2 methods:

cv::Mat1f convertImg2Mat(vigra::FImage &img){
    int b = img.width();
    int h = img.height();

    cv::Mat1f mat(h, b);

    for(unsigned int j=0; j<h; j++){
        for(unsigned int i=0; i<b; i++){
            mat(j,i) = img(i,j);
        }
    }
    return mat;
}

and

vigra::FImage convertMat2Img(cv::Mat mat){
    int b = mat.rows;
    int h = mat.cols;
    cv::Mat1f tmp = mat.clone();

    vigra::FImage img(h, b);

    for(unsigned int j=0; j<h; j++){
        for(unsigned int i=0; i<b; i++){
            img(i,j) = tmp(j,i);
        }
    }
    return img;
}

which both work fine.

A stupid beginners mistake was the indexing, because vigra follows fortran order which is

img(cols, rows)

and OpenCV uses another convention which is

mat(rows, cols).

So from my side this question is not yet propperly answered, but I found a working solution anyways.

Phtagen
  • 121
  • 10