11

I have a color that I want to convert to a different color space. Is it possible to use cvtColor on a cv::Vec3f directly without creating a 1x1 cv::Mat and populating it with that pixel, using cvtColor on the cv::Mat, then getting the only pixel out of the output? I have tried the following, but it doesn't seem to like getting passed a vector.

Any suggestions?

#include <iostream>

#include <opencv2/opencv.hpp>

int main(int, char*[])
{
    cv::Vec3f hsv;
    hsv[0] = .9;
    hsv[1] = .8;
    hsv[2] = .7;

    std::cout << "HSV: " << hsv << std::endl;

    cv::Vec3b bgr;
    cvtColor(hsv, bgr, CV_HSV2BGR); // OpenCV Error: Assertion failed (scn == 3 && (dcn == 3 || dcn == 4) && (depth == CV_8U || depth == CV_32F)) in cvtColor

    std::cout << "BGR: " << bgr << std::endl; 

    return EXIT_SUCCESS;
}

I also tried this, but get a different error:

#include <iostream>

#include <opencv2/opencv.hpp>

int main(int, char*[])
{
    cv::Mat_<cv::Vec3f> hsv(cv::Vec3f(0.7, 0.7, 0.8));

    std::cout << "HSV: " << hsv << std::endl;

    cv::Mat_<cv::Vec3b> bgr;

    cvtColor(hsv, bgr, CV_HSV2BGR); // OpenCV Error: Assertion failed (!fixedType() || ((Mat*)obj)->type() == mtype) in create

    std::cout << "BGR: " << bgr << std::endl;

    return EXIT_SUCCESS;
}
David Doria
  • 9,873
  • 17
  • 85
  • 147
  • @Nacho No, I don't see how that would do the actual conversion here? Could you post code showing how to use it to do a color space conversion? – David Doria Mar 02 '16 at 02:11
  • Why `cvtColor`? If you only want to convert one vector only, I figure it'd be easier to write a convert function. – Quang Hoang Mar 02 '16 at 02:13
  • @QuangHoang Because OpenCV already has tons of conversions written. It is usually fairly tricky to implement a color conversion because of all of the wrapping/edge cases, etc. – David Doria Mar 02 '16 at 02:14
  • @callyalater I have posted the errors on the lines that they occur. – David Doria Mar 02 '16 at 02:15
  • 1
    Your second approach is correct. Just use `cv::Mat_ bgr;` since source and destination must have the same type. Also, you can use `Mat3f` which is a typedef of `Mat_` – Miki Mar 02 '16 at 02:15
  • @Miki It's funny you replied - I was just looking at your profile this morning trying to figure out how to contact you. If you're interested in some contract work, please drop me a note - you can find my email address here: https://github.com/daviddoria – David Doria Mar 02 '16 at 02:18

1 Answers1

7

Your second approach is correct, but you have source and destination of different types in cvtColor, and that causes the error.

Be sure to have both hsv and bgr of the same type, CV_32F here:

#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
    cv::Mat3f hsv(cv::Vec3f(0.7, 0.7, 0.8));

    std::cout << "HSV: " << hsv << std::endl;

    cv::Mat3f bgr;
    cvtColor(hsv, bgr, CV_HSV2BGR); 

    std::cout << "BGR: " << bgr << std::endl;

    return 0;
}

You can use Mat3f for brevity. It's just a typedef:

typedef Mat_<Vec3f> Mat3f;
Miki
  • 40,887
  • 13
  • 123
  • 202
  • Is this allocating memory for 'hsv'? That is, say I already have a `cv::Vec3f` and I want to pass it to cvtColor. If I do `cv::Mat3f hsv(myVec)` will this slow down an inner loop? – David Doria Mar 02 '16 at 02:29
  • Yes, it's deep copying data. Yes, probably will slow down things. Easiest thing is: 1) don't use cvtColor, but apply directly the color conversion, 2) if you have a loop, you probably have a lot of `Vec3f`. You can make a matrix and pass the whole matrix without the loop – Miki Mar 02 '16 at 02:35
  • 1) You mean write it myself? Or is there a more direct way to call OpenCV's implementations? 2) I actually have an image, but I don't have enough memory to convert it to float (HSV) on a mobile device. Hence I was doing the conversions and my operations pixel-at-a-time so I could start with an 8bit image and end with a 8bit image and never actually need to construct the 32bit image. – David Doria Mar 02 '16 at 02:38
  • 1) Yes. You can just _borrow_ OpenCV implementation looking into `color.cpp`. AFAIK, if you want to use `cvtColor` you need a `Mat` or a `std::vector` object. 2) If you can show some code to get the idea we can look for an efficient way – Miki Mar 02 '16 at 02:45
  • Here's a follow up question that also basically demonstrates what I'm doing (just this in a loop over a whole image full of pixels): http://stackoverflow.com/questions/35737942/bgr-to-hsv-and-back-again - Any thoughts on this one? We really should try to get the documentation updated (it only has the forward ranges: http://docs.opencv.org/3.0-beta/modules/imgproc/doc/miscellaneous_transformations.html#void%20cvtColor%28InputArray%20src,%20OutputArray%20dst,%20int%20code,%20int%20dstCn%29) – David Doria Mar 02 '16 at 03:29
  • It seems that you're mixing range [0,1] for floats and range [0,255] for uchars. Also static cast seems wrong. I'll check better tomorrow ;D – Miki Mar 02 '16 at 03:42
  • Hm the static_cast actually seems to work as I'd expect (at least in a stand alone example I setup). Also, I am intentionally trying to convert the uchar BGR values to float HSV, as it seems uchar HSV loses some precision (representing S and V as an integer fraction of 255 cannot represent the full range of float values that are actually computed). – David Doria Mar 02 '16 at 13:39
  • Yep, static_cast works ok since OpenCV provides conversions operators between Vec of different types. I posted an answer there, now it seems to work as expected. I'll let you know about the email ;D – Miki Mar 02 '16 at 14:07
  • **Warning**: even though all you want is to convert a _single_ color into another colorspace, **`OpenCV::cvtColor` will take about 0.1 seconds.** Beware of that. – Константин Ван Jan 01 '21 at 04:04