2015-12-25 2 views
1

Je ne peux toujours pas accéder aux bits de couleur à partir d'une image bitmap. Le problème est que, après avoir sauvegardé le contenu du bitmap dans un tampon, je ne sais pas:Couleur bitmap - C

  1. Où commencer la boucle (Si je commence de 0 je pense qu'il efface les en-têtes)?

  2. Comment accéder aux octets et effectuez les modifications (transformer une couleur de la BMP dans un choix de la sortie)?

  3. Et, comment insérer le tampon dans un nouveau fichier bitmap?

Toutes les images I à modifier ont des lignes qui sont divisible par quatre (I avoir à insérer 0 quand un octet spécifique est rembourrage) et 24 bits par pixel. Même quelques conseils seraient très appréciés.

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
#include "bmp_header.h" 

int main(void) 
{ 
    FILE *f; 
    f = fopen("captcha.bmp","rb"); 
    if ((f = fopen("captcha.bmp", "rb")) == NULL) 
    { 
     printf("Error opening file %s.\n", "captcha.bmp"); 
     exit(1); 
    } 
    fread(&BMP_header,sizeof(BMP_header),1,f); 
    fread(&BMP_info_header,sizeof(BMP_info_header),1,f); 
    fseek(f,BMP_header.imageDataOffset,SEEK_SET); 
    int rows = (BMP_info_header.bitPix * BMP_info_header.width + 31) /32 * 4 ; 
    char *PixelArray =malloc(rows * abs(BMP_info_header.height)*sizeof(char)); 
    int i; 
    for(i =sizeof(BMP_header)+sizeof(BMP_info_header); i<=(rows * abs(BMP_info_header.height))-2;i+=3) 
    { 
     PixelArray[i]=255; // just a random value to test if this makese any sense 
     PixelArray[i+1]=255; 
     PixelArray[i+2]=255; 

    } 

    return 0; 
} 

Et, voici le contenu de bmp_header.h:

#pragma pack(1) 

struct bmp_fileheader 
{ 
    unsigned char fileMarker1; /* 'B' */ 
    unsigned char fileMarker2; /* 'M' */ 
    unsigned int bfSize; /* File's size */ 
    unsigned short unused1; 
    unsigned short unused2; 
    unsigned int imageDataOffset; /* Offset to the start of image data */ 
}BMP_header,BMP_header_out; 

struct bmp_infoheader 
{ 
    unsigned int biSize; /* Size of the info header - 40 bytes */ 
    signed int  width; /* Width of the image */ 
    signed int  height; /* Height of the image */ 
    unsigned short planes; 
    unsigned short bitPix; 
    unsigned int biCompression; 
    unsigned int biSizeImage; /* Size of the image data */ 
    int   biXPelsPerMeter; 
    int   biYPelsPerMeter; 
    unsigned int biClrUsed; 
    unsigned int biClrImportant; 
}BMP_info_header,BMP_info_header_out; 

#pragma pack() 
+3

Essayez de modifier un pixel au lieu de tous. Joyeux Noël btw. –

+0

@MillieSmith le problème est que la valeur d'une couleur est stockée dans 3 octets.Alors je ne peux pas faire cela .. –

+1

Vous n'avez pas à tout mettre en boucle cependant. Je suppose que je ne comprends pas cette déclaration: 'int rows = (BMP_info_header.bitPix * BMP_info_header.width + 31)/32 * 4;'. Pouvez-vous expliquer quelle est la signification de 31, 32 et 4 ici? –

Répondre

1

D'accord, je l'ai écrit un code pour écrire un fichier bitmap complètement noir avec les dimensions d'origine de l'ancien fichier bitmap.

fonctionne avec tous les types de bitmaps. Les bitmaps 16 couleurs, par exemple, ont une palette de couleurs après l'en-tête d'informations que mon programme ne prend pas en compte. Les bits par pixel doivent également être divisibles par 8. Si ces conditions préalables sont remplies, comme je crois qu'ils sont dans des bitmaps de 24 bits, alors ce programme devrait fonctionner.

Le code principal ici est en getNewImageData. Nous calculons la taille de la ligne pour l'image avec la même formule que Wikipédia utilise: elle calcule les bits requis, puis les place sur un multiple de quatre octets, puis convertit les bits en octets. Ensuite, nous avons mis à zéro toute la mémoire du réseau de pixels (la plupart du temps, je suis paranoïaque à propos de la sortie des valeurs dans les octets de pad). Ensuite, nous courons le long de chaque rangée et éditons chaque pixel. Le plus interne for loop correspond à chaque pixel. Une itération à travers le milieu for loop écrit à un pixel.

Vous pouvez évidemment modifier ce code pour lire les données de pixels dans une section malloced de mémoire, puis modifier les données de pixels en place avant de l'écrire arrière. Cet exemple de code ne lit pas les données de pixel d'entrée et écrit simplement un bitmap noir de mêmes dimensions que le fichier d'entrée.

Edit: Je suppose que je devrais mentionner ce que vous faites mal.

  • Vous avez appelé fopen deux fois, laissant un pointeur de fichier ouvert suspendu quelque part en mémoire.
  • rows devrait probablement être renommé rowSize puisque c'est la taille d'une ligne de pixels en octets
  • PixelArray n'est pas libéré.
  • Vos for loop commence à partir d'un décalage (la taille des deux têtes), puis est limitée par la taille des données de pixels. Le for loop devrait commencer à partir de 0 et aller à la taille des données de pixel (sauf si vous lisez les données de pixel dans ce que vous faites ... ce que vous ne devriez probablement pas faire).
  • Votre for loop essaie d'écrire simultanément sur toutes les données de pixel et ne tient pas compte du fait qu'il existe entre 0 et 31 bits de remplissage à la fin de chaque ligne de pixels (mon programme suppose que le remplissage est seulement 0, 8, 16 ou 24 bits).

Voici le code:

#include <stdio.h> 
#include "bmp_header.h" 
#include <stdlib.h> 

int readBitmapHeaders(char* fileLocation, bmp_fileheader* fileheader, bmp_infoheader* infoheader) 
{ 
    FILE* f; 
    f = fopen(fileLocation, "rb"); 

    if (!f) 
    { 
     printf("Error opening file %s.\n", fileLocation); 
     return 1; 
    } 

    fread(fileheader, sizeof(bmp_fileheader), 1, f); 
    fread(infoheader, sizeof(bmp_infoheader), 1, f); 

    fclose(f); 
    return 0; 
} 

int writeBitmap(char* fileName, bmp_fileheader* fileheader, bmp_infoheader* infoheader, char* pixelArray, size_t pixelArraySize) 
{ 
    FILE* out; 
    out = fopen(fileName, "wb"); 
    if (!out) 
    { 
     printf("Error opening file %s.\n", fileName); 
     return 1; 
    } 

    fwrite(fileheader, sizeof(bmp_fileheader), 1, out); 
    fwrite(infoheader, sizeof(bmp_infoheader), 1, out); 
    fwrite(pixelArray, pixelArraySize, 1, out); 

    fclose(out); 
    return 0; 
} 

char* getNewImageData(bmp_infoheader* infoheader, size_t* imageSize) 
{ 
    //rowsize is padded to 4 bytes 
    size_t rowSize = (infoheader->bitPix * infoheader->width + 31)/32 * 4; 

    size_t pixelArraySize = rowSize * abs(infoheader->height); 

    char* pixelArray = (char*)malloc(pixelArraySize); 
    if (!pixelArray) 
    { 
     return NULL; 
    } 

    memset(pixelArray, 0, pixelArraySize); 

    size_t bytesPerPixel = infoheader->bitPix/8; 
    for (int i = 0; i < infoheader->height; i++) 
    { 
     for (int j = 0; j < infoheader->width; j++) 
     { 
      size_t offset = rowSize * i + bytesPerPixel * j; 
      for (size_t k = 0; k < bytesPerPixel; k++) 
      { 
       pixelArray[offset + k] = 0; 
      } 
     } 
    } 

    if (imageSize) 
    { 
     *imageSize = pixelArraySize; 
    } 
    return pixelArray; 
} 

int main() 
{ 
    char* fileLocation = "test.bmp"; 
    bmp_fileheader header; 
    bmp_infoheader infoheader; 

    int readResult = readBitmapHeaders(fileLocation, &header, &infoheader); 
    if (readResult) 
    { 
     return readResult; 
    } 

    size_t pixelArraySize; 
    char* pixelArray = getNewImageData(&infoheader, &pixelArraySize); 
    if (!pixelArray) 
    { 
     printf("%s", "Failed to create the new image data. Exiting with fatal error.\n"); 
     return 1; 
    } 

    char* outFile = "out.bmp"; 
    int writeResult = writeBitmap(outFile, &header, &infoheader, pixelArray, pixelArraySize); 

    free(pixelArray); 
    return writeResult; 
} 

J'ai changé le fichier d'en-tête bitmap un peu typedef les struct et rendre la vie plus facile (au moins pour moi):

#pragma once 
#pragma pack(1) 

typedef struct _bmp_fileheader 
{ 
    unsigned char fileMarker1; /* 'B' */ 
    unsigned char fileMarker2; /* 'M' */ 
    unsigned int bfSize; /* File's size */ 
    unsigned short unused1; 
    unsigned short unused2; 
    unsigned int imageDataOffset; /* Offset to the start of image data */ 
} bmp_fileheader; 

typedef struct _bmp_infoheader 
{ 
    unsigned int biSize; /* Size of the info header - 40 bytes */ 
    signed int  width; /* Width of the image */ 
    signed int  height; /* Height of the image */ 
    unsigned short planes; 
    unsigned short bitPix; 
    unsigned int biCompression; 
    unsigned int biSizeImage; /* Size of the image data */ 
    int   biXPelsPerMeter; 
    int   biYPelsPerMeter; 
    unsigned int biClrUsed; 
    unsigned int biClrImportant; 
} bmp_infoheader; 

#pragma pack() 
+0

pourquoi attribuez-vous cette valeur? Qu'est-ce que rowSize a à voir avec la hauteur et la largeur? –

+0

La largeur est utilisée pour calculer l'attribut rowsize et peut être utilisée pour indexer une ligne. Les données de pixel sont un groupe de lignes dans la mémoire contiguë. Plus précisément, un total de rangées de hauteur. Le calcul trouve la ligne et le décalage corrects dans cette ligne pour le pixel spécifique à modifier. –

+0

Je dois à travers le rembourrage doit être ajouté à la fin de la rangée .. c'est ce que je ne comprends pas pourquoi vous l'avez mis dans la seconde pour. J'ai une autre clarté: où puis-je éditer le pixel (modifier les blancs dans une couleur spécifique)? dans la même seconde pour? –