0

I have a simple function that attempts to loop through all the pixels in a single channel cv::Mat. Its not functioning correctly. I'm running this in xcode on ios sim.

cv::Mat fillEdge(cv::Mat floated) {
    float currentColor = 255.0;
    cv::Size shape = floated.size();
    int h = shape.height;
    int w = shape.width;

    int count = 1;

    for(int y = 0; y!= h; y++) {
        for(int x = 0; x!= w; x++) {
            cv::Point2i p(y, x);
            floated.at<int>(p) = currentColor;
        }
    }
    std::cout << floated.channels() << std::endl;
    // prints 1


    std::cout << floated << std::endl;
    return floated;
}

For some reason it prints a striped image.

enter image description here enter image description here

Here is what the output of the cv::Mat looks like before the function returns

[255,   0,   0,   0, 255,   0,   0,   0, 255,   0,   0,   0, 255,   0,   0,   0, 255,
0,   0,   0, 255,   0,   0,   0, 255,   0,   0,   0, 255,   0,   0,   0, 255,   0,
0,   0, 255,   0,   0,   0, 255,   0,   0,   0, 255,   0,   0,   0, 255,   0,   0,
0, 255,   0,   0,   0, 255,   0,   0,   0, 255,   0,   0,   0, 255,   0,   0,   ...
Cristian
  • 495
  • 2
  • 9
  • 35

2 Answers2

2

You should use setTo:

cv::Mat fillEdge(cv::Mat floated) {
    float currentColor = 255.0;
    floated.setTo(cv::Scalar(currentColor));
    return floated;
}
Miki
  • 40,887
  • 13
  • 123
  • 202
  • This will surely change the value of all pixels, but we lose control over each independent pixel. If we want to add an `if` statement in the inner `for` loop to apply a new color only to the edges (as the name of the function suggests), then the `.at()` method should be used. – Vinícius Queiroz Nov 17 '18 at 19:46
  • @vini yes, of course. But the question explicitly asks for "all" pixels – Miki Nov 17 '18 at 23:23
0

Ok, I changed your for loops condition check, changed the definition of your Point2i and also changed the template type of your at method from int to float, to maintain type integrity.

This should work now:

cv::Mat fillEdge(cv::Mat floated) {
    float currentColor = 255.0;
    cv::Size shape = floated.size();
    int h = shape.height;
    int w = shape.width;

    int count = 1;

    for(int y = 0; y < h; y++) {
        for(int x = 0; x < w; x++) {
            cv:Point2i p = cv:Point2i(x,y);
            floated.at<float>(p) = currentColor;
            //floated.at<float>(y,x) = currentColor; //You could also replace the two lines above with direct indexing (be careful with the order of the axis, as pointed by @Micka in the comments)
        }
    }
    std::cout << floated.channels() << std::endl;
    // prints 1


    std::cout << floated << std::endl;
    return floated;
}
Vinícius Queiroz
  • 849
  • 1
  • 7
  • 19
  • 1
    p is wrong, it should be (x,y) order. Then you could use floated.at(p), too. But with direct indexing it is in the right ordering floated.at(y,x) which is usable, too. – Micka Nov 16 '18 at 06:47
  • Actually, in this case, declaration of `p`is useless. I put it that way just to denote how it should be declared in the first place, and then used direct indexing to show another way of using `.at`, not using points. But `p` should be declared as (y,x), as y (declared in the first loop) is ranging vertically (with height, not width), and the Point2i declaration is (row, column). Anyways, I'll update the answer. – Vinícius Queiroz Nov 17 '18 at 19:44
  • 1
    no, points are (x=col,y=row) and have (x,y) ordering in the constructor. that's what I wanted to tell you. See https://stackoverflow.com/q/25642532/2393191 – Micka Nov 17 '18 at 19:57
  • 1
    Oops, you're right! Thanks for the info!! Updated again! – Vinícius Queiroz Nov 17 '18 at 20:54