web-dev-qa-db-fra.com

Conversion de BufferedImage en Mat en opencv

Comment puis-je convertir une BufferedImage en un Mat dans OpenCV? J'utilise le Java wrapper pour OpenCV (pas JavaCV). Comme je suis nouveau sur OpenCV, j'ai quelques problèmes pour comprendre comment fonctionne Mat.

Je veux faire quelque chose comme ça. (D'après la réponse de Ted W.):

          BufferedImage image = ImageIO.read(b.getClass().getResource("Lena.png"));

          int rows = image.getWidth();
          int cols = image.getHeight();
          int type = CvType.CV_16UC1;
          Mat newMat = new Mat(rows,cols,type);

          for(int r=0; r<rows; r++){
              for(int c=0; c<cols; c++){
                  newMat.put(r, c, image.getRGB(r, c));
              }
          }

          Highgui.imwrite("Lena_copy.png",newMat);

Ça ne marche pas. "Lena_copy.png" est juste une image noire avec les bonnes dimensions.

28
Jompa234

J'essayais également de faire la même chose, en raison de la nécessité de combiner l'image traitée avec deux bibliothèques. Et ce que j'ai essayé de faire, c'est de mettre byte[] dans Mat au lieu de la valeur RVB. Et ça a marché! Donc ce que j'ai fait, c'est:

1.Converti BufferedImage en tableau d'octets avec:

byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();

2. Ensuite, vous pouvez simplement le mettre à Mat si vous définissez le type sur CV_8UC3

image_final.put(0, 0, pixels);

Edit: Vous pouvez également essayer de faire l'inverse comme sur cette réponse

25
andriy

Celui-ci a bien fonctionné pour moi, et il faut de 0 à 1 ms pour être exécuté.

public static Mat bufferedImageToMat(BufferedImage bi) {
  Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC3);
  byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
  mat.put(0, 0, data);
  return mat;
}
18
Jorge Mardones

Vous ne voulez pas gérer un grand tableau de pixels? Utilisez simplement ceci

BufferedImage to Mat

public static Mat BufferedImage2Mat(BufferedImage image) throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ImageIO.write(image, "jpg", byteArrayOutputStream);
    byteArrayOutputStream.flush();
    return Imgcodecs.imdecode(new MatOfByte(byteArrayOutputStream.toByteArray()), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
}

Mat à BufferedImage

public static BufferedImage Mat2BufferedImage(Mat matrix)throws IOException {
    MatOfByte mob=new MatOfByte();
    Imgcodecs.imencode(".jpg", matrix, mob);
    return ImageIO.read(new ByteArrayInputStream(mob.toArray()));
}

Remarque, bien que ce soit très négligeable. Cependant, de cette manière, vous pouvez obtenir une solution fiable mais elle utilise l'encodage + le décodage. Vous perdez donc des performances. C'est généralement de 10 à 20 millisecondes. JPG l'encodage perd de la qualité de l'image aussi c'est lent (cela peut prendre 10 à 20 ms). BMP est sans perte et rapide (1 ou 2 ms) mais nécessite un peu plus de mémoire (négligeable). PNG est sans perte mais un peu plus de temps pour encoder que BMP. L'utilisation de BMP devrait convenir à la plupart des cas, je pense.

14
Ultraviolet

J'ai trouvé une solution ici . La solution est similaire à Andriys.

Camera c;
c.Connect();
c.StartCapture();
Image f2Img, cf2Img;
c.RetrieveBuffer(&f2Img);
f2Img.Convert( FlyCapture2::PIXEL_FORMAT_BGR, &cf2Img );
unsigned int rowBytes = (double)cf2Img.GetReceivedDataSize()/(double)cf2Img.GetRows();

cv::Mat opencvImg = cv::Mat( cf2Img.GetRows(), cf2Img.GetCols(), CV_8UC3, cf2Img.GetData(),rowBytes );
3
Jompa234

J'utilise le code suivant dans mon programme.

protected Mat img2Mat(BufferedImage in) {
        Mat out;
        byte[] data;
        int r, g, b;

        if (in.getType() == BufferedImage.TYPE_INT_RGB) {
            out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC3);
            data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
            int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
            for (int i = 0; i < dataBuff.length; i++) {
                data[i * 3] = (byte) ((dataBuff[i] >> 0) & 0xFF);
                data[i * 3 + 1] = (byte) ((dataBuff[i] >> 8) & 0xFF);
                data[i * 3 + 2] = (byte) ((dataBuff[i] >> 16) & 0xFF);
            }
        } else {
            out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC1);
            data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
            int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
            for (int i = 0; i < dataBuff.length; i++) {
                r = (byte) ((dataBuff[i] >> 0) & 0xFF);
                g = (byte) ((dataBuff[i] >> 8) & 0xFF);
                b = (byte) ((dataBuff[i] >> 16) & 0xFF);
                data[i] = (byte) ((0.21 * r) + (0.71 * g) + (0.07 * b));
            }
        }
        out.put(0, 0, data);
        return out;
    }

Référence: ici

2
Karthik N G

Une façon simple serait de créer un nouveau

Mat newMat = Mat(rows, cols, type);

puis récupérez les valeurs en pixels de votre BufferedImage et mettez-les dans newMat en utilisant

newMat.put(row, col, pixel);
1
Ted W.