Send opencv Mat image through Qt websocket to HTML client - html

I'm trying to write an application in c++ using Qt 5.7, basically it should be a websocket server, using qwebsocket for this, capable to send an image elaborated with opencv to an HTML client. What I'm trying to do is encode the image in base64, transmit and on the client put the encoded string in the src of an image tag.
Just to test, I can send/receive text messages correctly, so the websocket architecture is working, but I have some problems with images. This is my code snippets:
Server
cv::Mat imgIn;
imgIn = cv::imread("/home/me/color.png",CV_LOAD_IMAGE_COLOR);
QByteArray Img((char*)(imgIn.data),imgIn.total()*imgIn.elemSize());
QByteArray Img64 = Img.toBase64();
pClient->sendBinaryMessage(Img64);
Client
<img id="ItemPreview" src="" style="border:5px solid black" />
....
websocket.binaryType = "arraybuffer";
websocket.onmessage = function (evt) {
console.log( "Message received :", evt.data );
document.getElementById("ItemPreview").src = "data:image/png;base64," + evt.data;
};
I think most of the problems are in the Server, because the base64 sequence I got from the image is different from the one I can get from online converter image/base64.
On the client I receive this error in the console and nothing is showed:
data:image/png;base64,[object ArrayBuffer]:1 GET
data:image/png;base64,[object ArrayBuffer] net::ERR_INVALID_URL
Any hints?
SOLUTION
Thanks to the suggestions, I can provide the working code:
Server
imgIn = cv::imread("/home/me/color.png", CV_LOAD_IMAGE_UNCHANGED);
std::vector<uchar> buffer;
cv::imencode(".png",imgIn,buffer);
std::string s = base64_encode(buffer.data(),buffer.size());
pClient->sendTextMessage(QString::fromStdString(s));
Client
Removed this line:
websocket.binaryType = "arraybuffer";
The base64 encoding in the server is done using this code:
Encode/Decode base64

This line in the server:
imgIn = cv::imread("/home/me/color.png",CV_LOAD_IMAGE_COLOR);
decodes a PNG formatted image, and places it in memory as load of pixel data (plus possibly some row padding, which you don't take account of, see below). That's what you're base64 encoding.
This line in the client:
document.getElementById("ItemPreview").src = "data:image/png;base64," + evt.data;
is expecting a PNG image, but that isn't what you're sending; you've just pushed out a load of raw pixel data, with no dimensions or stride or format information or anything else.
If your client wants a PNG, you're going to have to use something like imencode to write PNG data to a memory buffer, and base64 encode that instead.
One other important thing to note is that decoded images may have row padding... a few bytes on the end of each row for memory alignment purposes. Therefore, the actual length of each image row may exceed the width of the image multiplied by the size of the each pixel in bytes. That means that this operation:
QByteArray Img((char*)(imgIn.data),imgIn.total()*imgIn.elemSize());
may not, in fact, wrap the entire image buffer in your QByteArray. There are various ways to check the stride/step of an image, but you'd best read the cv::Mat docs as it isn't worth me repeating them all here. This only matters if you're doing raw byte-level image manipulation, like you are here. If you use imencode, you don't need to worry about this.

Related

Display image that returns HTTP 503 in Firefox

I have a status badge image that returns the HTTP code 503 when the respective service is offline (but the webserver is still there serving calls). Now opening the image URL directly will display the image properly, regardless of the underlying 503 error code. But using it inside an <img> tag shows the broken image icon. How can I prevent that while still allowing the image itself to return a 503? (External services depend on that)
Here are some screenshots to illustrate what's going on:
The badge on the page:
The status message in the developer console:
The badge itself:
Note: This happens on Firefox. Not Chrome
Edit: Here are a few requested pieces information:
Firefox 78.0.2 (64-Bit)
It's served from the same domain. But the domain is essentially just proxying serveral underlying webservices. And this badge is originating from a different service but all on the same domain.
It's a SVG image if that makes any difference.
Since XMLHttpRequest can retrieve the output of any request, no matter the response code, it is possible to request for the image with XMLHttpRequest, and then convert the blob response type to a base64 format image, which can be loaded in the browser.
The CORS proxy I used in the sample code may not be necessary in the majority of cases, but could be useful in the case where the image you are trying to display has weird response headers that prevent access to the image from another domain.
Here is the sample code. It should work no matter the response code, CORS, etc.
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var reader = new FileReader();
reader.onloadend = function () {
// here, reader.result contains the base64-formatted string you can use to set the src attribute with
document.getElementsByTagName('img')[0].src = reader.result; // sets the first <img> tag to display the image, change to the element you want to use
};
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', "https://cors-anywhere.herokuapp.com/i.stack.imgur.com/8wB1j.png"); // don't include the HTTP/HTTPS protocol in the url
xhr.responseType = 'blob';
xhr.setRequestHeader('X-Requested-With', 'xhr');
xhr.send();
<img src="about:blank">
Everything works, as when you go into Inspect Element, you see that the src attribute of the <img> tag points to a base64 URL that can load in any browser.
You might want to compress or resize your images before uploading it to server , as they might be large enough to keep the server busy and show the error as most of the time, a 503 error occurs because the server is too busy.
More over the image is SVG so it might render dimesions before completing, hence I'd suggest
Try replacing the SVG with PNG or JPG
Also try for site like https://tinypng.com/ to compress the image size
This might work for you

Find file format of loaded bitmap with SkiaSharp?

I'm trying to figure out the format of an image that is loaded by an array of bytes into an SKBitmap. I'd just like to know if it is a JPEG or PNG, etc.
// Was the decoding done by a JPEG decoder or PNG or?
var bitmap = SKBitmap.Decode(buffer);
Is this possible?
I would have a look at SKCodec.
https://learn.microsoft.com/dotnet/api/skiasharp.skcodec
That doesn't even load the actual image data out the box - just the headers. The way, you don't use stacks of memory when reading the file type.
To read the type, use the EncodedFormat property:
https://learn.microsoft.com/dotnet/api/skiasharp.skcodec.encodedformat
using var codec = SKCodec.Create(...);
var format = codec.EncodedFormat;

How does img srcs' internal algorithm work?

I am curious as to what browsers (Specifically chrome) are doing when you set an img tags src to a path which returns a png binary string vs. if you would call this endpoint manually (Say using Ajax or Axios) and then set the images src to this PNG binary manually. Because the results are different between the two.
I am running a Node/expressJs server, which has an image file which returns from the images/ path.
router.get('images/', async function(req, res) {
try {
return await fs
.createReadStream('./images/sample.png')
.pipe(res);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
This API call will return a PNG binary string.
scenario 1 - setting the img src to the endpoint where the PNG lives
If I will set the img element as such <img src="http://localhost/images/"
This call will return the PNG binary from my Node server and automatically display everything correctly.
scenario 2 - Using axios to request this PNG manually
If however, I would use a library like axios, to call this endpoint manually, I would get back a PNG binary string (In the chrome dev-tools 'network' tab, when looking at repsonse data, I even see a nice preview thumbnail of the image, so something in that panel is also correctly taking the PNG data and displaying it in an img element there). But if I will take this data and try to set it to the src of the image tag, I need to do all kinds of base64 conversions before it will work.
So my question is - What is the internal algo of the img element, for when it receives binary image data from a server?
There is no 'internal algorithm'. src is an HTML attribute that expects a URI, whether it is an HTTP URI or a Base64 URI. The reason you have to convert the data to Base64 if you request the image using JavaScript is because this is the most straightforward way to represent binary data as a URI. When the browser sees a Base64 URI, then it just converts it back into binary data and displays it.

How Does One Send BOTH HTML and Images Using libMongoose/Embedded Mongoose?

So the Mongoose.c library is pretty straight-forward. I've been able to use their event system, URL recognition, multi-form example, and their connection system to build a simple login-system. I've used C++ minGW, the mongoose.c&.h, and my browser. Now I'd like to implement images.
But there's a fundamental issue I can't get around. I can transfer EITHER an html document, OR an image. The jpg, alone, will display happily, as will the html document, so long as either is alone. My code is relatively simple, for html:
--pretend std::string HTMLAsString holds all html for the document.
mg_send_data(conn,HTMLAsString,strlen(HTMLAsString));
When I want to send an image, its quite similar:
while ((fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_send_data(conn,buf,n);
}
mg_send_data(conn,"\r\n",2);
Both of these work (I've cut out the irrelevant parts like how the string is composed, or how the buffer is filled, suffice to say those aspects work). I can have HTML formatting, with a 'missing image space,' or I can have an image shown, but no HTML.
How do I send BOTH an image and HTML?
Mr. Andersen should get credit for this, but I can't mark a comment as an answer, and I want to close the question.
He was dead on. First the client-browser requests the page. The server sends it. When the client-browser receives the HTML document, it then sends requests to the server for all images/files as specified in the HTML.
I was checking all requests from clients for the addresses, using conn->uri. This allowed me to simply run string comparisons to figure out what page I was receiving data from. However, I wasn't checking for any OTHER strings apart from those I had pages for.
As soon as I put a simple:
std::cout << "REQUESTED:" << conn->uri << std::endl;
I saw the requests clear as day (in my case /image.jpg). So I put the aforementioned image code together with just another string comparison in the reply function, and presto-magico, images embedded within HTML, all playing nice and happy together.
Thank you for answering my question.
P.S. The send file code is a little different:
char buf[1024];
int n;
FILE *fp;
fp = fopen(cstrpath, "rb");
if(fp==NULL){printf("ERROR, NO %s found.",cstrpath);}
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_send_data(conn,buf,n);
}
fclose(fp);
mg_send_data(conn,"\r\n",2);

How to display images from an email with QWebView?

I have an email which contains perfectly formatted html with the single exception that images are linked differently: <img width=456 height=384 id="_x0000_i1026" src="cid:X.MA2.1374935634#aol.com" alt="cid:X.MA4.1372453963#aol.com"> the email has other parts including the image with this content id. The problem is that I dont know how to point the QWebview to the data (which I have). Is there a way to add the image to its cache?
It's possible but not easy.
Basically you need to:
1- provide your own QNetworkAccessManager-inherited class, overriding createRequest() to catch these links refering to "cid":
QNetworkReply*
MyManager::createRequest (Operation op,
const QNetworkRequest & req,
QIODevice * outgoingData = 0)
{
if (op==GetOperation && req.url().scheme() == "cid")
return MyNetworkReply(req.url().path());
else
return QNetworkAccessManager::createRequest(op, req, outgoingData);
}
2- Connect it to the webview with:
MyManager* manager = new MyManager;
view->page()->setNetworkAccessManager(manager);
3- Provide an implementation of MyNetworkReply which inherits from QNetworkReply, a QIODevice-class. And this is the complicated part. You need to provide at least readData(), bytesAvailable(), a constructor that sets up the reply in terms of HTTP headers, and launches the actual asynchronous read with QTimer::singleShot()
4- Decode the attachment (probably from base64 if it's a picture) into a QByteArray for your MyNetworkReply::readData() to read from that.
There's a complete example on qt.gitorious.org written by Qt Labs developers in the Qt 4.6 days. They display an internally generated PNG, not an external mail attachment, but the general steps are as described above. See:
http://qt.gitorious.org/qt-labs/graphics-dojo/blobs/master/url-rendering/main.cpp
However this code has a flaw with Qt-4.8. in the constructor for RendererReply, when it does:
open(ReadOnly|Unbuffered);
this should be:
open(ReadOnly);
otherwise webkit never reads the entire data and displays the broken picture icon.