I have an application where I send base64 data of images from my OpenCV C++ code to an AWS URL which will find the age and gender of the faces in the images and send the results back in a JSON format.
I use cURL library to send the data through POST request. This part works fine, I am able to send the data and it the image is getting saved correctly every frame.
For reading the JSON data, I use the jsoncpp library to parse the JSON data.
I want to integrate both the parts in one function. This works as well when I send a single image. I have an issue when I send multiple images in a loop and receive their JSON responses. I keep receiving the first JSON response every time. I have tested this by generating random numbers and sending that as a JSON response each time on the server side and when I receive it in my C++ code, I only get the first JSON response, ie the first number, every time. But when I try receiving JSON data without sending POST data and just send a GET request, I receive the JSON data correctly every time. My code for both the parts integrated is here:
#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include <curl/curl.h>
#include <jsoncpp/json/json.h>
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "base64.h"
using namespace std;
using namespace cv;
/*Converts Mat to base64 string*/
string image_to_base64(Mat image)
{
string base64Output;
vector<uchar> vec_frame;
vector<int> vecCompression_params;
vecCompression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
vecCompression_params.push_back(90);
imencode(".jpg", image, vec_frame, vecCompression_params);
uchar *enc_msg = new uchar[vec_frame.size()];
for(int i=0; i < vec_frame.size(); i++)
enc_msg[i] = vec_frame[i];
base64Output = base64_encode(enc_msg, vec_frame.size());
std::replace(base64Output.begin(), base64Output.end(), '+', '-');
std::replace(base64Output.begin(), base64Output.end(), '/', '_');
std::replace(base64Output.begin(), base64Output.end(), '=', '*');
return base64Output;
}
/**Sends the base64 string to AWS**/
struct WriteThis
{
const char *readptr;
long long sizeleft;
};
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct WriteThis *pooh = (struct WriteThis *)userp;
if(size*nmemb < 1)
return 0;
if(pooh->sizeleft)
{
*(char *)ptr = pooh->readptr[0]; /* copy one single byte */
pooh->readptr++; /* advance pointer */
pooh->sizeleft--; /* less data left */
return 1; /* we return 1 byte at a time! */
}
return 0; /* no more data left to deliver */
}
string cURL_JSON_data;
size_t writeJSONURLCallback(char* buf, size_t size, size_t nmemb, void* up)//Callback function to store the URL's data
{
for (int c = 0; c<size*nmemb; c++)
{
cURL_JSON_data.push_back(buf[c]);
}
return size*nmemb;
}
string jsonTest;
tuple<string, string> send_base64(string base64_string)
{
CURL *curl;
CURLcode res;
long long int httpCode(0);
unique_ptr<std::string> httpData(new std::string());
string age_info, gender_info;
struct WriteThis pooh;
//cout<<"base64_string.length(): "<<base64_string.length();
char data[base64_string.length()];
strcpy(data, base64_string.c_str());
pooh.readptr = data;
pooh.sizeleft = (long long)strlen(data);
//cout<<"strlen(data): "<<strlen(data)<<endl;
/* In windows, this will init the winsock stuff */
res = curl_global_init(CURL_GLOBAL_DEFAULT);
/* Check for errors */
if(res != CURLE_OK)
{
fprintf(stderr, "curl_global_init() failed: %s\n",
curl_easy_strerror(res));
//return 1;
}
/* get a curl handle */
curl = curl_easy_init();
if(curl)
{
/* First set the URL that is about to receive our POST. */
curl_easy_setopt(curl, CURLOPT_URL, "MYURL");
/* Now specify we want to POST data */
curl_easy_setopt(curl, CURLOPT_POST, 1L);
/* we want to use our own read function */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
/* pointer to pass to our read function */
curl_easy_setopt(curl, CURLOPT_READDATA, &pooh);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeJSONURLCallback);//writes the conents of url
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //output cURL's progress
curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
/* get verbose debug output please */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/*
If you use POST to a HTTP 1.1 server, you can send data without knowing
the size before starting the POST if you use chunked encoding. You
enable this by adding a header like "Transfer-Encoding: chunked" with
CURLOPT_HTTPHEADER. With HTTP 1.0 or without chunked transfer, you must
specify the size in the request.
*/
#ifdef USE_CHUNKED
{
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "Transfer-Encoding: chunked");
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
/* use curl_slist_free_all() after the *perform() call to free this
list again */
}
#else
/* Set the expected POST size. If you want to POST large amounts of data,
consider CURLOPT_POSTFIELDSIZE_LARGE */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (pooh.sizeleft));
#endif
#ifdef DISABLE_EXPECT
/*
Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue"
header. You can disable this header with CURLOPT_HTTPHEADER as usual.
NOTE: if you want chunked transfer too, you need to combine these two
since you can only set one list of headers with CURLOPT_HTTPHEADER. */
/* A less good option would be to enforce HTTP 1.0, but that might also
have other implications. */
{
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "Expect:");
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
/* use curl_slist_free_all() after the *perform() call to free this
list again */
}
#endif
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
//cout<<"Testing_1"<<endl;
curl_easy_cleanup(curl);
}
curl_global_cleanup();
Json::Reader jsonReader;
Json::Value jsonData;
if (jsonReader.parse(cURL_JSON_data, jsonData))
{
cout << "Successfully parsed JSON data" << std::endl;
cout << "\nJSON data received:" << std::endl;
jsonTest = jsonData.toStyledString();
cout << jsonData.toStyledString() << std::endl;
age_info = jsonData["Age"].asString();
gender_info = jsonData["Gender"].asString();
}
else
{
cout<<"Could not parse JSON data"<<endl;
}
jsonData.clear();
return make_tuple(age_info, gender_info);
}
int main()
{
Mat frame;
string base64_data,age, gender;
int count = 0;
VideoCapture cap(1);
while(1)
{
cap>>frame;
count++;
resize(frame, frame, Size(frame.cols / 2, frame.rows / 2));
if(count % 10 ==0)//Sending the data every 10 frames
{
base64_data = image_to_base64(frame);
tie(age, gender) = send_base64(base64_data);
cout<<"age: "<<age<<", gender: "<<gender<<endl;
}
imshow("window", frame);
waitKey(30);
}
return 0;
}
And the code I use for receiving JSON with only GET request is here:
#include <iostream>
#include <stdlib.h>
#include <string>
#include <memory>
#include <curl/curl.h>
#include <jsoncpp/json/json.h>
using namespace std;
string cURLdata;
size_t writeURLCallback(char* buf, size_t size, size_t nmemb, void* up)
{
for (int c = 0; c<size*nmemb; c++)
{
cURLdata.push_back(buf[c]);
}
return size*nmemb;
}
int main()
{
int count = 0;
while(1){
CURL* curl; //curl object
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, "MYURL");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeURLCallback);//writes the conents of url
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //output cURL's progress
curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_global_cleanup();
Json::Reader jsonReader;
Json::Value jsonData;
bool parsedSuccess = jsonReader.parse(cURLdata,jsonData,false);
if (parsedSuccess)
{
std::cout << "Successfully parsed JSON data" << std::endl;
std::cout << "\nJSON data received:" << std::endl;
cout << jsonData.toStyledString() << std::endl;
}
else
{
std::cout << "Could not parse HTTP data as JSON" << std::endl;
}
jsonData.clear();
cURLdata.clear();
}
return 0;
}
Can someone help me point out what I'm doing wrong?