web-dev-qa-db-fra.com

Comment lire les pixels JPEG et PNG en C ++ sous Linux?

Je fais un traitement d'image et j'aimerais lire individuellement chaque valeur de pixel dans une image JPEG et PNG.

Dans mon scénario de déploiement, il serait gênant pour moi d'utiliser une bibliothèque tierce (car j'ai un accès restreint sur l'ordinateur cible), mais je suppose qu'il n'y a pas de bibliothèque C ou C++ standard pour lire JPEG/PNG ...

Donc, si vous connaissez un moyen de pas utiliser une bibliothèque, c'est parfait, sinon les réponses sont toujours les bienvenues!

22
Nick Bolton

Il n'y a pas de bibliothèque standard dans la norme C pour lire les formats de fichiers.

Cependant, la plupart des programmes, en particulier sur la plate-forme Linux, utilisent la même bibliothèque pour décoder les formats d'image:

Pour jpeg c'est libjpeg, pour png c'est libpng.

Les chances que les bibliothèques soient déjà installées sont très élevées.

http://www.libpng.org

http://www.ijg.org

21
Nils Pipenbrinck

Ceci est une petite routine que j'ai creusée à partir d'un code source de 10 ans (en utilisant libjpeg):

#include <jpeglib.h>

int loadJpg(const char* Name) {
  unsigned char a, r, g, b;
  int width, height;
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;

  FILE * infile;        /* source file */
  JSAMPARRAY pJpegBuffer;       /* Output row buffer */
  int row_stride;       /* physical row width in output buffer */
  if ((infile = fopen(Name, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", Name);
    return 0;
  }
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_decompress(&cinfo);
  jpeg_stdio_src(&cinfo, infile);
  (void) jpeg_read_header(&cinfo, TRUE);
  (void) jpeg_start_decompress(&cinfo);
  width = cinfo.output_width;
  height = cinfo.output_height;

  unsigned char * pDummy = new unsigned char [width*height*4];
  unsigned char * pTest = pDummy;
  if (!pDummy) {
    printf("NO MEM FOR JPEG CONVERT!\n");
    return 0;
  }
  row_stride = width * cinfo.output_components;
  pJpegBuffer = (*cinfo.mem->alloc_sarray)
    ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  while (cinfo.output_scanline < cinfo.output_height) {
    (void) jpeg_read_scanlines(&cinfo, pJpegBuffer, 1);
    for (int x = 0; x < width; x++) {
      a = 0; // alpha value is not supported on jpg
      r = pJpegBuffer[0][cinfo.output_components * x];
      if (cinfo.output_components > 2) {
        g = pJpegBuffer[0][cinfo.output_components * x + 1];
        b = pJpegBuffer[0][cinfo.output_components * x + 2];
      } else {
        g = r;
        b = r;
      }
      *(pDummy++) = b;
      *(pDummy++) = g;
      *(pDummy++) = r;
      *(pDummy++) = a;
    }
  }
  fclose(infile);
  (void) jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);

  BMap = (int*)pTest; 
  Height = height;
  Width = width;
  Depth = 32;
}
20
Peter Parker

Pour jpeg, il existe déjà une bibliothèque appelée libjpeg , et il y a libpng pour png. La bonne nouvelle est qu'ils compilent directement et que les machines cibles n'auront donc pas besoin de fichiers dll ou quoi que ce soit. La mauvaise nouvelle est qu'ils sont en C :(

Aussi, ne pensez même pas à essayez de lireles fichiers vous-même. Si vous voulez un format facile à lire, utilisez plutôt PPM .

7
rlbond

Malheureusement, le format jpeg est compressé, vous devrez donc le décompresser avant de lire les pixels individuels. Il s'agit d'une tâche non triviale. Si vous ne pouvez pas utiliser une bibliothèque, vous pouvez en consulter une pour voir comment elle décompresse l'image. Il existe une bibliothèque open source sur sourceforge: CImg sur sourceforge.

4
Colin

Comme l'a souligné Nils, il n'existe pas de bibliothèque standard C ou C++ pour la compression JPEG et la manipulation d'images.

Dans le cas où vous seriez en mesure d'utiliser une bibliothèque tierce, vous voudrez peut-être essayer GDAL qui prend en charge JPEG, PNG et des dizaines d'autres formats, compressions et médiums.

Voici un exemple simple qui présente comment lire les données de pixels à partir d'un fichier JPEG à l'aide de l'API GDAL C++:

#include <gdal_priv.h>
#include <cassert>
#include <iostream>
#include <string>
#include <vector>

int main()
{
    GDALAllRegister(); // once per application

    // Assume 3-band image with 8-bit per pixel per channel (24-bit depth)
    std::string const file("/home/mloskot/test.jpg");

    // Open file with image data
    GDALDataset* ds = static_cast<GDALDataset*>(GDALOpen(file.c_str(), GA_ReadOnly));
    assert(0 != ds);

    // Example 1 - Read multiple bands at once, assume 8-bit depth per band
    {
        int const ncols = ds->GetRasterXSize();
        int const nrows = ds->GetRasterYSize();
        int const nbands = ds->GetRasterCount();
        int const nbpp = GDALGetDataTypeSize(GDT_Byte) / 8;
        std::vector<unsigned char> data(ncols * nrows * nbands * nbpp);

        CPLErr err = ds->RasterIO(GF_Read, 0, 0, ncols, nrows, &data[0], ncols, nrows, GDT_Byte, nbands, 0, 0, 0, 0);
        assert(CE_None == err);

        // ... use data
    }

    // Example 2 - Read first scanline by scanline of 1 band only, assume 8-bit depth per band
    {
        GDALRasterBand* band1 = ds->GetRasterBand(1);
        assert(0 != band1);

        int const ncols = band1->GetXSize();
        int const nrows = band1->GetYSize();
        int const nbpp = GDALGetDataTypeSize(GDT_Byte) / 8;
        std::vector<unsigned char> scanline(ncols * nbpp);

        for (int i = 0; i < nrows; ++i)
        {
            CPLErr err = band1->RasterIO(GF_Read, 0, 0, ncols, 1, &scanline[0], ncols, 1, GDT_Byte, 0, 0);
            assert(CE_None == err);

            // ... use scanline
        }
    }

    return 0;
}

Il y a plus complet tutoriel API GDAL disponible.

2
mloskot

Puisqu'il pourrait utiliser l'exposition, je mentionnerai une autre bibliothèque pour enquêter: The IM Toolkit , qui est hébergé à Sourceforge . Il est multiplateforme et résume complètement le format de fichier loin de l'utilisateur, permettant à une image d'être chargée et traitée sans se soucier de la plupart des détails. Il prend en charge les formats PNG et JPEG prêts à l'emploi et peut être étendu avec d'autres filtres d'importation si nécessaire.

Il est également livré avec une grande collection d'opérateurs de traitement d'image ...

Il a également une liaison de bonne qualité pour Lua .

2
RBerteig

Si la vitesse n'est pas un problème vous pouvez essayer LodePNG qui adoptent une approche très minimaliste du chargement et de l'enregistrement PNG.

Ou même aller avec picoPNG du même auteur qui est un chargeur png autonome dans une fonction.

1
Gigi

J'ai eu de bonnes expériences avec la bibliothèque DevIL . Il prend en charge un large éventail de formats d'image et suit un style de fonction très similaire à OpenGL.

Certes, c'est une bibliothèque, mais ça vaut vraiment la peine d'essayer.

1
Daniel Sloof

Puisque les autres réponses mentionnent déjà que vous aurez très probablement besoin d'utiliser une bibliothèque, jetez un œil à ImageMagick et voyez s'il est possible de faire ce que vous avez besoin de faire. Il est livré avec une variété de façons d'interfacer avec les fonctionnalités de base d'ImageMagick, y compris des bibliothèques pour presque tous les langages de programmation disponibles.

Page d'accueil: ImageMagick

1
X-Istence