Search code examples
c++opencvazure-cognitive-servicesopencv3.1cpprest-sdk

Send image from OpenCV 3 to Cognitive Face API using C++ REST SDK


I want to use the Microsoft Face API from an application in C++. The cpprest sdk allows me to send an url of image or binary data of image. The problem is that my image is not a file in disk but a cv::Mat in memory. I have been trying to serialize it via an stringstream, but the request method complains because only accepts some strings and istream.

The following code is good when opening an image from file:

file_stream<unsigned char>::open_istream(filename)
 .then([=](pplx::task<basic_istream<unsigned char>> previousTask)
 {
    try
    {
       auto fileStream = previousTask.get();

       auto client = http_client{U("https://api.projectoxford.ai/face/v0/detections")};

       auto query = uri_builder()
          .append_query(U("analyzesFaceLandmarks"), analyzesFaceLandmarks ? "true" : "false")
          .append_query(U("analyzesAge"), analyzesAge ? "true" : "false")
          .append_query(U("analyzesGender"), analyzesGender ? "true" : "false")
          .append_query(U("analyzesHeadPose"), analyzesHeadPose ? "true" : "false")
          .append_query(U("subscription-key"), subscriptionKey)
          .to_string();

       client
          .request(methods::POST, query, fileStream)
   ...
    }
}

Here a file_stream is used to open the file. I tried serializing my Mat like this:

    // img is the cv::Mat
    std::vector<uchar> buff;
    cv::imencode(".jpg", img, buff);
    std::stringstream ssbuff;
    copy(buff.begin(), buff.end(), std::ostream_iterator<unsigned char>(ssbuff,""));

This serialization works as I can decode if after and rebuild the image.

¿How can I send to server the opencv Mat image through the client?


Solution

  • This question here (best way to upload uint8_t array to azure blob storage with wastorage in c++) lead me to the final answer.

    The request method for client asks for a concurrency::streams::istream object when passing raw data (see docs here: https://microsoft.github.io/cpprestsdk/classweb_1_1http_1_1client_1_1http__client.html#a5195fd9b36b8807456bd529e3cdc97f5) So, the stringstream containing the bytearray must be passed to an bytestream object provided by the SDK and open it with an istream object (provided by the SDK too).

    concurrency::streams::bytestream byteStream = 
    concurrency::streams::bytestream();
    concurrency::streams::istream inputStream = 
    byteStream.open_istream(ssbuff.str());
    auto fileStream = inputStream;
    // Build uri and so on ...
    client.request(methods::POST, query, fileStream)
    // async processing ...
    

    The type of the request is not needed to be explicit, as it is by default as "application/octet-stream" according to docs.