Hi everyone, welcome to the last part of this tutorial. Today, we are going to finish our client, adding the faces that were replied by the Watson API, then we will add some style to our client, making it more "beautiful". As we saw on the last tutorial, our server is completely done, when we send an image to it, it replies us with a JSON object, this is the response that we received on the last tutorial:
Let us take a look on this JSON, as we can see, we have an array called images, this array separates every processed image, but we implemented our server just to processes one image for each request however, Watson API allows us to send as many images as we want. Inside each element images, we have an array of faces, this array keeps all the faces that were recognized by the API for that image.
Inside each face, we have only 3 information, age, face_location and gender. Let us understand each of them:
- Age - It is the possible age of the face, it is divided in 3 values, min, max and score. Min and Max are the minimum and the maximum possible ages, respectively. The Score is the probability of the API is right about those two values (min and max). If we take a look at our JSON, we can see that the face is between 44 and 48 years old with a probability of 68% certain.
- Gender - It is the possible gender of the face, it is divided in 2 values, gender (MALE or FEMALE) and score which is the probability of certain about the gender. In that case, we have a male's face with a probability of 99% certain.
- face_location - This is the most important information that we have, it is the face's location. It tells us the position and the size to build a box on the face. The values left and top are the horizontal and vertical position, respectively and the width and height are the horizontal and vertical size, respectively. Both values are relative to the image, with them we are able to build a box inside the image.
The JSON that we saw contains only one face, although if we send an image with 2 faces to the server, how is going to be the JSON? Let us test it. Below we have a JSON Object which has 2 faces:
As we can see, the only difference between the first one and the second one, is the faces array, the second one contains 2 faces. Now, we know what each element inside the response means, we can start to implement it, let us go!!!
Rendering the Faces
First, we have to change our index.js to take and save the server's response when we make a request, then we have to pass it to our ImageUploader component. To do that, we are going to create a new state for our application called response. Replace the index.js constructor with the code below:
On this code, we just initialized the state response with undefined as default value. Now, we have to change the function changeHandle to set the state when we receive the response from the server. Change the changeHandle function to be like the code below:
Here, we first added a setState before we make the request, this setState sets the state response to undefined, doing that, we will clean all faces were rendered before. Then, we changed the success's callback, adding another setState, but this one takes the server's response and saves inside response. After all these changes, our application is able to keeps the server's response every time that we make a request. To finish it, let us just pass this data to the ImageUploader, we will do that using properties, like the code below:
Our index.js is completely done, now we have to implement the face's rendering on the ImageUploader.js. We will do that using a DIV for each face and then applying it a style with the width, height, left and top which can be found inside the response. First of all, we will create the function that will render the faces, inside the script ImageUploader, add the function renderFaces, like this:
This is the function that we will use to render our faces, let us understand how it works. First, we check if the property data is valid (lines 3 and 4), if it is not valid we do not draw anything. If our data is valid, we will map all the faces (line 8 to 15) and then, we render they passing the css properties left, top, width and height that were calculated by the server. With these changes, we are almost finishing it, we just have to change our render to be like the code below:
Now, our client will render all faces inside the image, but we need to do one more thing. If we execute our application now, it will not be able to render the faces correctly, do you know why? Because we do not have any style sheet for it, we need to create a style sheet to set the absolute position and to center our component. Inside our component's folder, create a new CSS file that will be called style.css and put the code below inside it:
This style will center our component and will position our faces in the right place. Let us understand how it is working:
- Line 2 - We set our main DIV's position as absolute, we did that to be sure that our faces will be relative to the main DIV.
- Line 3 and 4 - Here, we centered our component in the middle of the screen. First, we set the property left as 50%, this property is relative to the width of the screen, when we set it as 50% we are moving our component 50% of the screen's width to the right. But there is a problem with it, if we only use this property, our component will not be centered. To center that, we need to use another property called transform together with the left. When we make a translate using the operator %, we are moving the component relative to itself, so if we make a -50% translate on the horizontal axis, we are positioning our component on the right place. This technique is also known as a CSS trick to centralize an absolute component.
- Line 8 and 9 - On these lines, we set the position of our faces as absolute and we add a white dashed border for them, creating a beautiful box around the face.
- Line 13 - Still talking about our face's box, here we create a cool effect using the hover event. With this, when we pass the mouse over the face's box, we are going to set a white color with transparency as the background of it, this effect makes our component more intuitive and cool.
- Line 17 to 19 - This piece of code adds the color, the size and a text shadow effect on the face's name, it is a text inside the face's box that we use as an identifier for each face.
Now, that is the time to test, let us test it using an image with only one face. The application should work like this:
It is working perfectly! Now, let us test with an image that has more than one face, let us see how the result is going to be:
GREAT!!!!!! it recognized all the 5 faces correctly. On the next step, we will create a function to show the information (age and gender) of each face inside a table, so let us do it.
Showing the face's information
To show the information, we have to create a new component that will render a table containing all the information. This component do not need to have state, so we can create only a function instead of a class, it allows us to use less memory and it is a good practice. First, create a new JS file called FaceInformation.js inside our folder components, then add the code below inside it:
This kind of component is known as Stateless Component because you do not have any state for this, it just has what is passed via properties. The code above is really simple to understand, it just checks if there is information to be rendered, if it does not have, the component renders nothing. The function renderInformation is used to render the information about the faces, this function will return an array with the rows that must be rendered. Now we just need to create the import and the call to the FaceInformation in the index.js, add the code below inside it:
We just added the import(Line 1) and the FaceInformation inside our render(Line 7) passing the server's response as property, it is enough to render that. On The next step, we are going to create a CSS style to make this table cooler, so add the CSS below inside the style.css:
This CSS will make our table pretty cool, it basically sets the size of the table and creates a strip effect and a hover effect on the rows. It makes our table more readable. Now, it is done, let us test! If you followed all the steps, your application would be like this:
Look, all the information that Watson brought us are being shown by our application, now we will make some adjustments in the CSS to make our application cooler, let us do it!
Adjusting the style
Let us start changing the Upload Button, we need to position it under the image and then center it, although, our button does not have a class or ID which allows us to create an unique style for it. So, we will start adding a className for it, just change the method render inside our ImageUploader.js to be like this:
As we can see, on the line 6, we are adding the className upload-button to our button, now we can create an unique style for it. Open the style.css and put this style:
On the code above, we are setting a fixed size and a block display to jump the line, we are setting color, font and border too. We added a background-color transition to our button, making an effect when the user puts the mouse over the button. Now, it is the time to see the result of this style, run the application and it will be something like this:
It is much better now! The last thing we need to do is to set a max width and height to our image. But, first we have to add a className for it, open the ImageUploader.js and change the render to be like the code below:
Now, our IMG component has the className image-uploader, so we will add a CSS style to limit the size of our image, inside our style.css, add this style:
Now it is going to work, is not it? No, it is not going to work, let us see why! If you start the application and try to upload an image, you will see this problem:
Look, the face's box position is completely wrong, but why does it happen? It happens because we are using the data which was generated from the original image, in an image that has its size limited. For example, we upload an image that is 1280x800, the Watson API will recognize the face and then will reply us the face's positions, but the face's positions are relative to the original image, when we limit the size of our image we are trying to render a face which was calculated using the dimensions 1280x800 in an image with other dimensions. That is the problem!
This problem could be solved in a simple way, what we need to do is to get the image's original dimensions and the image's current dimensions, with these 2 dimensions we can calculate a scale factor. We will use this formula to calculate our factor currentDimension / originalDimension, and then, when we are rendering the faces, we need to multiply the face's position by the factor. Let us do it now, inside our ImageUploader.js we need to do 3 things, first, we have to create a new function that will be called on the img's onLoad event, this function is going to be in charge of calculating our factor, we need to create a binding inside our constructor too. Then we need to change the function renderFaces to use the factor when it renders the faces, and the last thing is to add the onLoad event on our function render. It is going to be like the code below:
If you followed all the steps rightly, when you start the application and upload an image it should work like the image below:
It is done, our server and client are both working pretty fine. In this tutorial we saw how good Watson API is. This tutorial just showed one of its features, however there are lots of great features that you can use in your projects, from AI Assistant to Speech Converter. On this link, you can see all the Watson's features. Feel free to learn about them if you want.
See you in the next tutorial!