web-dev-qa-db-fra.com

Comment faire correspondre cv2.imread à la sortie keras image.img_load

J'étudie l'apprentissage profond. Formation d'un algorithme de classification d'images. Le problème est cependant que pour former des images, j'ai utilisé:

test_image = image.load_img('some.png', target_size = (64, 64))
test_image = image.img_to_array(test_image)

Alors que pour une application réelle, j'utilise:

test_image = cv2.imread('trick.png')
test_image = cv2.resize(test_image, (64, 64))

Mais j'ai trouvé que ceux-ci donnent un ndarray différent (données différentes):

Dernières entrées de load_image:

  [ 64.  71.  66.]
  [ 64.  71.  66.]
  [ 62.  69.  67.]]]

Dernières entrées de cv2.imread:

  [ 15  23  27]
  [ 16  24  28]
  [ 14  24  28]]]

, donc le système ne fonctionne pas. Existe-t-il un moyen de faire correspondre les résultats de l'un à l'autre?

11
wasd

OpenCV lit les images au format BGR alors qu'en kéros, il est représenté en RVB. Pour que la version OpenCV corresponde à l'ordre que nous attendons (RGB), inversez simplement les canaux:

test_image = cv2.imread('trick.png')
test_image = cv2.resize(test_image, (64, 64))
test_image = test_image[...,::-1] # Added

La dernière ligne inverse les canaux pour qu'ils soient dans l'ordre RVB. Vous pouvez ensuite l'intégrer dans votre modèle de keras.

J'aimerais ajouter un autre point: cv2.imread lit généralement les images dans uint8 précision. En examinant la sortie de votre image chargée en kéros, vous pouvez voir que les données sont en précision à virgule flottante, vous pouvez donc également convertir en une représentation à virgule flottante, telle que float32:

import numpy as np
# ...
# ...
test_image = test_image[...,::-1].astype(np.float32)

Enfin, selon la façon dont vous avez formé votre modèle, il est généralement habituel de normaliser les valeurs des pixels de l'image en [0,1] intervalle. Si vous avez fait cela avec votre modèle de kéros, assurez-vous de diviser vos valeurs par 255 dans votre image lue via OpenCV:

import numpy as np
# ...
# ...
test_image = (test_image[...,::-1].astype(np.float32)) / 255.0
10
rayryeng

Outre CV2 en utilisant le format BGR et Keras (en utilisant PIL comme backend) en utilisant le format RVB, il existe également des différences significatives dans les méthodes de redimensionnement de CV2 et PIL en utilisant les mêmes paramètres.

Plusieurs références peuvent être trouvées sur Internet, mais l'idée générale est qu'il existe des différences subtiles dans les systèmes de coordonnées de pixels utilisés dans les deux algorithmes de redimensionnement et également des problèmes potentiels avec différentes méthodes de transtypage pour flotter comme étape intermédiaire dans l'algo d'interpolation. Le résultat final est une image visuellement similaire mais légèrement décalée/perturbée entre les versions.

Un exemple parfait d'une attaque contradictoire qui peut entraîner d'énormes différences de précision malgré de petites différences d'entrée.

5
Kyle Vrooman

Récemment, je suis tombé sur le même problème. J'ai essayé de convertir le canal de couleur et de redimensionner l'image avec OpenCV. Cependant, PIL et OpenCV ont des façons très différentes de redimensionner l'image. Voici la solution exacte à ce problème.

C'est la fonction qui prend chemin du fichier image, convertit à la taille ciblée et se prépare pour le modèle Keras -

import cv2
import keras
import numpy as np
from keras.preprocessing import image
from PIL import Image

def prepare_image (file):
    im_resized = image.load_img(file, target_size = (224,224))
    img_array = image.img_to_array(im_resized)
    image_array_expanded = np.expand_dims(img_array, axis = 0)
    return keras.applications.mobilenet.preprocess_input(image_array_expanded)

# execute the function
PIL_image = prepare_image ("lena.png")

Si vous avez une image OpenCV, la fonction sera comme ceci -

def prepare_image2 (img):
    # convert the color from BGR to RGB then convert to PIL array
    cvt_image =  cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    im_pil = Image.fromarray(cvt_image)

    # resize the array (image) then PIL image
    im_resized = im_pil.resize((224, 224))
    img_array = image.img_to_array(im_resized)
    image_array_expanded = np.expand_dims(img_array, axis = 0)
    return keras.applications.mobilenet.preprocess_input(image_array_expanded)

# execute the function
img = cv2.imread("lena.png")
cv2_image = prepare_image2 (img)

# finally check if it is working  
np.array_equal(PIL_image, cv2_image)
>> True
1
Sourav