Reputation: 25
I would like to render an image on React returned from FastAPI backend using StreamingResponse
. The image is in the form of a numpy
array, which is of cv2
type of object.
@app.post("/predict")
async def root(file: UploadFile = File(...)):
global model
global store_coordinates
global store_faces
global store_mesh
content = await file.read()
nparr = np.fromstring(content, np.uint8)
bg_img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
......................
for i in range(len(store_coordinates)):
x, y, w, h = store_coordinates[i]
bg_img [b:b + d, a:a + c] = store_mesh[i]
res,im_png = cv2.imencode(".png", bg_img)
return StreamingResponse(io.BytesIO(im_png.tobytes()), media_type="image/png")
Here, I have created an API endpoint in which the uploaded image is received using POST
request, and a StreamableResponse(Image)
is returned.
How can I render this returned response on React frontend?
React Code:
import React, { Component } from "react";
import axios from "axios";
class Detect extends Component {
state = {
title: "",
content: "",
image: null,
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value,
});
};
handleImageChange = (e) => {
this.setState({
image: e.target.files[0],
});
};
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state);
let form_data = new FormData();
form_data.append("image", this.state.image, this.state.image.name);
let url = "http://127.0.0.1:8000/predict";
axios
.post(url, form_data, {
headers: {
"content-type": "multipart/form-data",
},
})
.then((res) => {
console.log(res.data);
})
.catch((err) => console.log(err));
};
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit}>
<p>
<input
type="file"
id="image"
accept="image/png, image/jpeg"
onChange={this.handleImageChange}
required
/>
</p>
<input type="submit" />
</form>
<div id="image-render">
<img></img>
</div>
</div>
);
}
}
export default Detect;
I would like to render the returned image in the div
tag which has the id
of image-render
.
Edit - This is the response I get from my backend.
Upvotes: 1
Views: 6194
Reputation: 34385
There are two options:
base64_string= base64.b64encode(image_bytes).decode('utf-8')
return base64_string
which can then be used to display the image in an HTML page as shown
here, for instance:
<img src="data:image/png;base64,ADD_BASE64_STRING_HERE>
StreamingResponse
(as shown in the
example you provided) for sending the raw bytes, as, in your case, the
entire image bytes are already loaded into memory. Thus, you should rather
use a custom Response
, as explained in this answer and this
answer, for example:
return Response(image_bytes, media_type='image/jpeg')
On client side (using JavaScript), convert the raw bytes into either a
base64-encoded string (using btoa()
, String.fromCharCode()
and Uint8Array
), or a Blob
object (and then call
URL.createObjectURL() to create a URL representing the Blob
object).The examples below demonstrate how to implement the two methods explained in Option 2 above (i.e., having the server sending raw image bytes and, on client side, converting the image bytes to either a base64-encoded string or Blob
object), using either Fetch API
or Axios
library.
fetch('/predict', {
method: 'POST',
body: formData,
})
.then(response => response.blob())
.then(blob => {
var blobURL = URL.createObjectURL(blob);
var image = document.getElementById("myImage");
image.onload = function(){
URL.revokeObjectURL(this.src); // release the blob URL once the image is loaded
}
image.src = blobURL;
})
.catch(error => {
console.error(error);
});
fetch('/predict', {
method: 'POST',
body: formData,
})
.then(response => {
contentType = response.headers.get('content-type')
return response.arrayBuffer();
})
.then(arrayBuffer => {
base64string = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)))
var image = document.getElementById("myImage");
image.src = "data:" + contentType + ";base64," + base64string;
})
.catch(error => {
console.error(error);
});
Remember to define an <img>
tag in your HTML file, where you wish to display the image:
<img id="myImage" src="">
axios({
method: 'POST',
url: '/upload',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
},
responseType: "blob"
})
.then(response => {
var blobURL = URL.createObjectURL(response.data);
var image = document.getElementById("myImage");
image.onload = function(){
URL.revokeObjectURL(this.src); // release the blob URL once the image is loaded
}
image.src = blobURL;
})
.catch(error => {
console.error(error);
});
axios({
method: 'POST',
url: '/predict',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
},
responseType: "arraybuffer"
})
.then(response => {
base64string = btoa(String.fromCharCode(...new Uint8Array(response.data)))
contentType = response.headers['content-type']
return base64string;
})
.then(base64string => {
var image = document.getElementById("myImage");
image.src = "data:" + contentType + ";base64," + base64string;
})
.catch(error => {
console.error(error);
});
Remember to define an <img>
tag in your HTML file, where you wish to display the image:
<img id="myImage" src="">
Upvotes: 5