2017-01-01 2 views
0

J'essaie d'afficher un aperçu d'une image RAW 16 bits dans mon application WPF mais cela ne fonctionne pas comme je l'espérais. J'ai jeté un coup d'oeil à d'innombrables exemples, questions/réponses ici sur SA et même sur CodeProject mais cela ne m'a pas pris loin non plus. Eh bien, en fait, je l'ai fait depuis que je peux charger complètement une image RAW en niveaux de gris 8 bits et l'afficher sur l'application sans aucun artefact bizarre, mais quand il s'agit d'images 16 bits, tout est foiré.WPF C# - Chargement de 16bit (8bit multicanal) RAW Heightmap dans BitmapSource

Voici un exemple de la façon dont il devrait ressembler: https://i.stack.imgur.com/kSCX5.jpg

Et ce que je reçois en essayant de charger une version 16 bits de la même image brute (directement exportée sous forme 16bit d'un programme tiers): Le chargement de l'image 16 bits en 8 bits me donne la moitié de l'image correcte.

Ce sont mes fonctions d'importation:

BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open)); 

    private void LoadRAWData8bit() 
    { 
     int bits = format.BitsPerPixel; 
     int stride = ((width * bits + (bits - 1)) & ~(bits - 1))/8; 

     byte[] pixels = new byte[stride * height]; 

     for (int i = 0; i < br.BaseStream.Length; ++i) 
      pixels[i] = br.ReadByte(); 

     br.Close(); 

     BitmapSource bmp = BitmapSource.Create(width, height, 96, 96, format, null, pixels, stride); 

     RawFile = new CRAW(new Size(width, height), format, stride, bmp); 
    } 

    // 16bit load Test 
    private void LoadRAWData16bit() 
    { 
     WriteableBitmap wBmp = new WriteableBitmap(width, height, 96, 96, format, null); 
     int bpp = format.BitsPerPixel/8; 

     byte[] pixels = new byte[wBmp.PixelWidth * wBmp.PixelHeight]; 

     Int32Rect drawRegionRect = new Int32Rect(0, 0, wBmp.PixelWidth, wBmp.PixelHeight); 

     for (int i = 0; i < br.BaseStream.Length; ++i) 
      pixels[i] = br.ReadByte(); 

     br.Close(); 

     int stride = wBmp.PixelWidth * bpp; 
     wBmp.WritePixels(drawRegionRect, pixels, stride, 0); 

     RawFile = new CRAW(new Size(width, height), format, stride, wBmp); 
    } 

(. S'il vous plaît ne me dérange pas les paramètres passés à la classe CRAW, je n'expérimenter pour l'instant jusqu'à ce que je peux obtenir que cela fonctionne correctement)

Ai-je manqué une étape? Est-ce que quelqu'un sait comment faire fonctionner cela? J'ai essayé de faire la même fonction d'importation 8bit avec un tableau ushort au lieu d'un tableau d'octets:

private void LoadRAWData16bit() 
    { 
     int bits = format.BitsPerPixel; 
     int stride = ((width * bits + (bits - 1)) & ~(bits - 1))/8; 
     int bpp = format.BitsPerPixel/8; 

     ushort[] pixels = new ushort[stride * height * bpp]; 

     for (int i = 0; i < br.BaseStream.Length; i += sizeof(ushort)) 
      pixels[i] = br.ReadUInt16(); 

     br.Close(); 

     BitmapSource bmp = BitmapSource.Create(width, height, 96, 96, format, null, pixels, stride); 

     RawFile = new CRAW(new Size(width, height), format, stride, bmp); 
    } 

comme ça, mais cela me donne la moitié de l'image et il est beaucoup plus sombre que c'est censé être parce que il y a 1 octet tous les 2 octets dans le tableau ayant une valeur de 0. Faire des pixels [i/2] me redonne la même image que précédemment (échantillon de chargement de 16 bits). Chargement de l'image brute 16 bits dans Photoshop montre la boîte de dialogue d'importation qui suggère un compte de canal de 2 comme 8 bits (le régler à 16 bits avec 2 canaux ne me permet pas de l'importer, mais 16 bits avec 1 canal fonctionne). Cela expliquerait pourquoi le chargement de l'image 16 bits en tant que 8bit dans mon application WPF n'en importe que la moitié .. mais cela me laisse un gros point d'interrogation quant à savoir pourquoi il ne charge pas le 2ème canal aussi.

Ce sujet est peu documenté et à peine discuté nulle part. Je ne trouve rien sur ce sujet. J'espère trouver quelqu'un qui pourra m'aider.

Merci et bonne année!

Répondre

0

J'ai trouvé la solution par moi-même en analysant l'image .RAW dans un éditeur Hex.

Fondamentalement, une image multi-canal 8/16 bits RAW est structurée comme représenté sur cette capture d'écran (8 bits par exemple): http://puu.sh/t97fY/6920f804af.png

Le premier octet appartient à l'image elle-même (également appelé Alpha 1 selon Photoshop), le second octet est le deuxième canal (appelé Alpha 2). Les octets de chaque couche d'image sont alternés comme indiqué dans la capture d'écran. Dans la capture d'écran ci-dessus, les octets 0x00 appartiennent au deuxième canal tandis que les autres octets (59, 57, 58, 56, etc.) appartiennent à l'image principale.

Diviser les 2 couches est assez simple à ce stade. Depuis BitmapSource.Create génère un bitmap à partir d'octets bruts, tout ce que nous avons à faire est de faire une boucle dans le FileStream et d'alterner le buffer de destination pour ajuster les octets dans leurs tampons corrects qui seront ensuite fournis à la classe BitmapSource. Dans le cas où l'image RAW n'a pas de canal alpha, mais juste l'image simple elle-même, vous pouvez allouer un tampon aussi grand que l'image normale et le remplir avec 0x00 qui vous donnera une couche alpha vide.

Comment déterminer par programme si une image RAW a un canal alpha est également facile. Étant donné la largeur et la hauteur, une image RAW multicanal sera deux fois plus grande que l'image de base.

Exemple:

Width: 128 
Height: 128 
RAW Image Size: 32768 
128 * 128 = 16384 

Si

file_size == largeur * hauteur

alors nous avons pas de canal alpha.

Si

file_size == largeur * hauteur * 2

nous avons un canal alpha.

Espérons que cela peut aider quelqu'un dans le futur. Il y a tellement de formats d'image RAW et cela peut devenir assez déroutant très rapidement surtout pour ceux qui travaillent avec Heightmaps/Photoshop RAW en essayant de manipuler ces images par programmation .. Ces formats ne semblent pas bien documentés ... ou peut-être que je Je n'ai pas assez creusé, même si j'ai passé un après-midi entier sur ce sujet.