web-dev-qa-db-fra.com

Comment convertir un octet [] en BufferedImage en Java?

Je poste ce fil parce que j'ai quelques difficultés à gérer les images en Java. J'aimerais pouvoir convertir une image en tableau d'octets [], puis pouvoir effectuer l'opération inverse pour pouvoir modifier le RVB de chaque pixel, puis créer une nouvelle image. Je souhaite utiliser cette solution car setRGB () et getRGB () de BufferedImage sont peut-être trop lentes pour les grandes images (corrigez-moi si je me trompe).

J'ai lu quelques articles ici pour obtenir un tableau d'octets [] ( comme ici ) de sorte que chaque pixel soit représenté par 3 ou 4 cellules du tableau contenant les valeurs rouge, verte et bleue (avec l'alpha valeur, quand il y a 4 cellules), ce qui est très utile et facile à utiliser pour moi. Voici le code que j'utilise pour obtenir ce tableau (stocké dans une classe PixelArray que j'ai créée):

public PixelArray(BufferedImage image)
{
    width = image.getWidth();
    height = image.getHeight();
    DataBuffer toArray = image.getRaster().getDataBuffer();
    array = ((DataBufferByte) toArray).getData();
    hasAlphaChannel = image.getAlphaRaster() != null;
}

Mon gros problème est que je n'ai trouvé aucune méthode efficace pour convertir ce tableau d'octets [] en une nouvelle image, si je voulais transformer l'image (par exemple, supprimez les valeurs bleu/vert et ne gardez que la rouge). J'ai essayé ces solutions:

1) Créer un objet DataBuffer, puis créer un SampleModel, pour créer finalement un WritableRaster puis un BufferedImage (avec des objets ColorModel et Hashtable supplémentaires). Cela n'a pas fonctionné parce que, apparemment, je n'ai pas toutes les informations dont j'ai besoin (je ne sais pas du tout quel est le Hashtable pour le constructeur BufferedImage ()).

2) Utilisation d'un ByteArrayInputStream. Cela n'a pas fonctionné car le tableau byte [] attendu avec ByteArrayInputStream n'a rien à voir avec le mien: il représente chaque octet du fichier, et non chaque composant de chaque pixel (avec 3-4 octets pour chaque pixel) ...

Quelqu'un pourrait-il m'aider?

21
Jef Grailet

Essaye ça:

private BufferedImage createImageFromBytes(byte[] imageData) {
    ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
    try {
        return ImageIO.read(bais);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
32
heejong

J'ai essayé les approches mentionnées ici, mais pour une raison quelconque, aucune d'entre elles n'a fonctionné. Utiliser ByteArrayInputStream et ImageIO.read(...) renvoie null, alors que byte[] array = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); renvoie une copie des données d'image et non une référence directe à ces dernières (voir aussi ici ).

Cependant, ce qui suit a fonctionné pour moi. Supposons que les dimensions et le type des données d'image soient connus. Soit aussi byte[] srcbuf la mémoire tampon des données à convertir en BufferedImage. Ensuite,

  1. Créer une image vide, par exemple

    img=new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    
  2. Convertissez le tableau de données en Raster et utilisez setData pour remplir l’image, c.-à-d.

    img.setData(Raster.createRaster(img.getSampleModel(), new DataBufferByte(srcbuf, srcbuf.length), new Point() ) );
    
8
fyts
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
byte[] array = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(pixelArray, 0, array, 0, array.length);

Cette méthode a tendance à se désynchroniser lorsque vous essayez d'utiliser l'objet Graphics de l'image résultante. Si vous devez dessiner au-dessus de votre image, construisez une deuxième image (qui peut être persistante, c'est-à-dire non construite à chaque fois mais réutilisée) et drawImage la première.

6
Mark Jeronimus

Plusieurs personnes ont voté en faveur du commentaire selon lequel la réponse acceptée est fausse.

Si la réponse acceptée ne fonctionne pas, c'est peut-être parce que Image.IO ne prend pas en charge le type d'image que vous essayez (par exemple, tiff).

Ajoutez ceci à votre pom (alias jai-imageio-core-1.3.1.jar dans votre classpath):

    <!-- https://mvnrepository.com/artifact/com.github.jai-imageio/jai-imageio-core -->
    <dependency>
        <groupId>com.github.jai-imageio</groupId>
        <artifactId>jai-imageio-core</artifactId>
        <version>1.3.1</version>
    </dependency>

Pour ajouter un support pour:

  • wbmp
  • bmp
  • pcx
  • pnm
  • brut
  • tiff
  • gif (écrire)

Vous pouvez vérifier la liste des formats pris en charge avec:

     for(String format : ImageIO.getReaderFormatNames())
        System.out.println(format);

Notez qu'il vous suffit de déposer par exemple jai-imageio-core-1.3.1.jar dans votre chemin d'accès aux classes pour le faire fonctionner. 

Parmi les autres projets qui apportent un soutien supplémentaire, citons:

2
Jeffrey Knight

L’approche en utilisant ImageIO.read directement n’est pas correcte dans certains cas. Dans mon cas, l'octet brut [] ne contient aucune information sur la largeur, la hauteur et le format de l'image. En utilisant uniquement ImageIO.read, il est impossible pour le programme de construire une image valide.

Il est nécessaire de transmettre les informations de base de l'image à l'objet BufferedImage:

 BufferedImage outBufImg = new BufferedImage (largeur, hauteur, bufferedImage.TYPE_3BYTE_BGR); 

Définissez ensuite les données de l'objet BufferedImage à l'aide de setRGB ou setData. (Lors de l'utilisation de setRGB, il semble que nous devions d'abord convertir d'octet [] en int []. En conséquence, des problèmes de performances pourraient survenir si les données de l'image source sont volumineuses. Peut-être que setData est une meilleure idée pour les données source de type octet [] .)

0
Wagner