What is the use of applying img_to_array() after cv2.imread()
This is because of the data type you provide to the imshow() function.
Check the documentation:

The function may scale the image, depending on its depth:
If the image is 8-bit unsigned, it is displayed as is. If the image is
16-bit unsigned or 32-bit integer, the pixels are divided by 256. That
is, the value range [0,255*256] is mapped to [0,255]. If the image is
32-bit or 64-bit floating-point, the pixel values are multiplied by
255. That is, the value range [0,1] is mapped to [0,255].

So when you use cv2.imread() it reads data uint8, that is 8-bit unsigned format thus imshow() displays the original data.
But when you use img_to_array() it converts data to the float. Thus imshow() multiplies it with 256. It expects the float values in normalized form, that is all the pixels are divided by 256. That is the reason for such a display.
This is how cv2.imread() data looks like...

and its data type...

This is how img_to_array() data looks like...

and its data type...

If you divide your image by 1.0 to convert it to the float...
import cv2
# from keras.preprocessing.image import img_to_array
image = cv2.imread(&quot;car.jpg&quot;)
image = image/1.0
cv2.imshow(&quot;Divided by 1.0&quot;, image)
cv2.waitKey(0)

...this is what you will get:

Same as img_to_array.
But it you divide image with 256.0 as follows:
import cv2
# from keras.preprocessing.image import img_to_array
image = cv2.imread(&quot;car.jpg&quot;)
image = image/256.0
cv2.imshow(&quot;Divided by 256.0&quot;, image)
cv2.waitKey(0)

You get the original image since imshow() multiplies the float with 256.

So what you need is to divide your img_to_array() output by 256 or convert it to the uint8.