I'm writing a BMP image parser, I wrote the read and write functions, which works fine while reading B&W, grayscale, and color images. I'm writing a function for fragment a given image in 8x8 pixel images, so, if I have a 72x48 image, I will have 56 images. I already crop the pixels block, but when I write the images, these images have colors that the original didn't have, or are completely black.
The original image, the first 8x8 fragment, the second (the rest of the fragments are equals to this) 8x8 fragment.
I'm pretty confused because I already can read and copy the original image and write a copy.
Also, I crop an 8x8 fragment with a web tool, and some headers differ from the header that I calc. For example, the size of the whole file, which appears next to the image type is 172, while I calc 92. The header size is also different.
In order to fragment the image, I calc the number of fragments by multiplying the number of row by the number of columns, then I allocate memory for a list of images, where the fragments are saved, then I start a 3 nested for loop, where the first one is for allocated every image in the list and is jointed to the fragment number, the second and the third one is dedicated to copy pixel by pixel from the original to the block.
Could someone give a hint of what I could be doing wrong?
bmp.h
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <string.h>
typedef uint16_t ImageType;
typedef struct
{
uint32_t size;
uint16_t additionalFeature;
uint16_t copy;
uint32_t offset;
} ImageHeader;
typedef struct
{
uint32_t headerSize;
int width;
int height;
uint16_t colorSpaces;
uint16_t bitsPerPixel;
uint32_t compression;
uint32_t size;
int verticalResolution;
int horizontalResolution;
uint32_t totalColors;
uint32_t importantColors;
} ImageMetadata;
typedef struct
{
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t reserved;
} ImageColor;
typedef ImageColor *ImageColors;
typedef char *ImagePixels;
typedef struct
{
ImageType type;
ImageHeader header;
ImageMetadata metadata;
ImageColors colors;
ImagePixels pixels;
} Image;
ImageType BMP_IMAGE_TYPE = 0x4D42;
unsigned int getImagePixelsSize(int width, int height, int bitsPerPixel);
unsigned int getImageSize(Image image);
void readImage(char *filename, Image *image);
void writeImage(char *filename, Image image);
void fragmentImage(Image image, Image *images, int fragmentWidth, int fragmentHeight);
void showImage(Image image);
bmp.c
#include "bmp.h"
unsigned int getImagePixelsSize(int width, int height, int bitsPerPixel)
{
return height * (int)(((bitsPerPixel * width) + 31) / 32) * 4;
}
unsigned int getImageSize(Image image)
{
return (
getImagePixelsSize(image.metadata.width, image.metadata.height, image.metadata.bitsPerPixel) +
sizeof(image.type) +
sizeof(image.header) +
sizeof(image.metadata) +
sizeof(image.colors)
);
}
void showImage(Image image)
{
printf("Size: %d \n", image.header.size);
printf("Offset: %d", image.header.offset);
printf("Width: %d \n", image.metadata.width);
printf("Height: %d \n", image.metadata.height);
printf("BPP: %d \n", image.metadata.bitsPerPixel);
printf("Header size: %d \n", image.metadata.headerSize);
printf("Total colors: %d \n", image.metadata.totalColors);
printf("Raw colors: %lu \n", sizeof(image.colors));
printf("Pixel size %d \n", getImagePixelsSize(image.metadata.width, image.metadata.height, image.metadata.bitsPerPixel));
printf("Calculated Image Size: %d \n", getImageSize(image));
}
void readImage(char *filename, Image *image)
{
FILE *imageFile = fopen(filename, "r");
if (!imageFile)
{
perror("ReadImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->type), sizeof(ImageType), 1, imageFile);
if (image->type != BMP_IMAGE_TYPE)
{
fprintf(stderr, "%hu", image->type);
perror("ReadImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->header), sizeof(ImageHeader), 1, imageFile);
fread(&(image->metadata), sizeof(ImageMetadata), 1, imageFile);
if (image->metadata.bitsPerPixel <= 8)
{
image->colors = (ImageColor *)malloc(sizeof(ImageColor) * image->metadata.totalColors);
printf("Calculated raw colors %lu", sizeof(ImageColor) * image->metadata.totalColors);
fread(image->colors, sizeof(ImageColor) * image->metadata.totalColors, 1, imageFile);
}
int pixelsSize = getImagePixelsSize(image->metadata.width, image->metadata.height, image->metadata.bitsPerPixel);
image->pixels = (char *)malloc(sizeof(char) * pixelsSize);
if (!image->pixels)
{
perror("ReadImagePixelsException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fseek(imageFile, image->header.offset, SEEK_SET);
fread(image->pixels, sizeof(char) * pixelsSize, 1, imageFile);
fclose(imageFile);
}
void writeImage(char *filename, Image image)
{
FILE *imageFile = fopen(filename, "w+");
if (!imageFile)
{
perror("WriteImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
int typeWritten = fwrite(&(image.type), sizeof(ImageType), 1, imageFile);
if (typeWritten == 0)
{
perror("WriteImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
int headerWritten = fwrite(&(image.header), sizeof(ImageHeader), 1, imageFile);
if (headerWritten == 0)
{
perror("WriteImageHeaderException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
int metadataWritten = fwrite(&(image.metadata), sizeof(ImageMetadata), 1, imageFile);
if (metadataWritten == 0)
{
perror("WriteImageMetadataException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
if (image.metadata.bitsPerPixel <= 8)
{
int colorsWritten = fwrite(image.colors, sizeof(ImageColor) * image.metadata.totalColors, 1, imageFile);
if (colorsWritten == 0)
{
perror("WriteImageColorException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
}
int pixelsSize = getImagePixelsSize(image.metadata.width, image.metadata.height, image.metadata.bitsPerPixel);
int pixelsWritten = fwrite(image.pixels, sizeof(char) * pixelsSize, 1, imageFile);
if (pixelsWritten == 0)
{
perror("WriteImagePixelsException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fclose(imageFile);
}
void fragmentImage(Image image, Image *images, int fragmentWidth, int fragmentHeight)
{
if (fragmentWidth > image.metadata.width)
{
fprintf(stderr, "FragmentSizeException: Fragment width is larger than image width");
}
if (fragmentHeight > image.metadata.height)
{
fprintf(stderr, "FragmentSizeException: Fragment height is larger than image width");
}
int rows = (int)(image.metadata.width / fragmentWidth);
int columns = (int)(image.metadata.height / fragmentHeight);
int fragments = rows * columns;
// 72 x 48 -> 8x8
// 54
// images[54]
images = (Image *)malloc(sizeof(Image) * fragments);
if (!images)
{
perror("FragmentImageMemoryException");
exit(EXIT_FAILURE);
}
int w = 0, h = 0;
for (int fragment = 0; fragment < fragments; fragment++)
{
(images + fragment)->type = image.type;
(images + fragment)->colors = image.colors;
(images + fragment)->header = image.header;
(images + fragment)->metadata = image.metadata;
(images + fragment)->metadata.width = fragmentWidth;
(images + fragment)->metadata.height = fragmentHeight;
int pixelsSize = getImagePixelsSize(fragmentWidth, fragmentHeight, image.metadata.bitsPerPixel);
(images + fragment)->pixels = (char *)malloc(sizeof(char) * pixelsSize);
if (!(images + fragment)->pixels)
{
perror("FragmentImageMemoryException");
exit(EXIT_FAILURE);
}
for (int j = 0; j < 8; j++, w++)
{
for (int k = 0; k < 8; k++, h++)
{
(images + fragment)->pixels[(w * 8) + k] = image.pixels[(w * image.metadata.width) + k];
}
}
(images + fragment)->header.size = getImageSize(*(images + fragment));
char *filename = malloc(sizeof(char) * 38);
snprintf(filename, 38, "./images/fragments/fragment-%d.bmp", fragment);
writeImage(filename, *(images + fragment));
}
}
main.c
#include "bmp.c"
#include <math.h>
int main(int argc, char *argv[])
{
Image image;
Image *images;
char *imageSourceName = argv[1];
char *imageTargeName = argv[2];
readImage(imageSourceName, &image);
writeImage(imageTargeName, image);
showImage(image);
fragmentImage(image, images, 8, 8);
return 0;
}
Related
Hy I wanted to read a bmp file into a struct and write it then back but the image is alway black the header is ok. but the pixels get wrong written. I compared the hex values and they are the same until the header is finished. The rest is different and way shorter.
typedef uint16_t ImageType;
typedef struct
{
uint32_t size;
uint16_t additionalFeature;
uint16_t copy;
uint32_t offset;
} ImageHeader;
typedef struct
{
uint32_t headerSize;
int width;
int height;
uint16_t colorSpaces;
uint16_t bitsPerPixel;
uint32_t compression;
uint32_t size;
int verticalResolution;
int horizontalResolution;
uint32_t totalColors;
uint32_t importantColors;
} ImageMetadata;
typedef struct {
uint8_t blue;
uint8_t green;
uint8_t red;
} ImageColors;
typedef struct {
ImageType type;
ImageHeader header;
ImageMetadata metadata;
ImageColors **pixels;
} Image;
Here is my image structure. At the moment it is only for a 24 bpp Image later on I want to change it. But my problem is that after I wrote the image it is not correctly displayed. The header Data are ok but the image is not correct.
the rows are correct only the column is some how duplicated and compressed.
My includes are:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdbool.h>
This is how I read the pixels
void readImage(char *filename, Image *image)
{
FILE *imageFile = fopen(filename, "rb");
if (!imageFile)
{
perror("ReadImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->type), sizeof(ImageType), 1, imageFile);
//validate for correct image type
if (image->type != BMP_IMAGE_TYPE)
{
fprintf(stderr, "%hu", image->type);
perror("ReadImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->header), sizeof(ImageHeader), 1, imageFile);
fread(&(image->metadata), sizeof(ImageMetadata), 1, imageFile);
// Allocate space for the pixels.
image->pixels = malloc( image->metadata.height * sizeof(ImageColors *) );
for(int i = 0; i < image->metadata.height; i++){
image->pixels[i] = malloc(image->metadata.width * sizeof(ImageColors));
}
// Read in each pixel
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
ImageColors px;
if( fread(&px, sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while reading bmp pixel.\n");
return;
}
image->pixels[i][j] = px;
}
}
fclose(imageFile);
}
This is how I write them.
void writeImage(char *filename, Image *image)
{
FILE *imageFile = fopen(filename, "wb+");
if (!imageFile)
{
perror("WriteImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
size_t typeWritten = fwrite(&(image->type), sizeof(image->type), 1, imageFile);
if (typeWritten == 0)
{
perror("WriteImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
size_t headerWritten = fwrite(&(image->header), sizeof(image->header), 1, imageFile);
if (headerWritten == 0)
{
perror("WriteImageHeaderException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
size_t metadataWritten = fwrite(&(image->metadata), sizeof(image->metadata), 1, imageFile);
if (metadataWritten == 0)
{
perror("WriteImageMetadataException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fseek(imageFile, image->header.offset, SEEK_SET);
// Read in each pixel
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
if( fwrite(&image->pixels[i][j], sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while wr bmp pixel.\n");
fclose(imageFile);
exit(EXIT_FAILURE);
}
}
}
fclose(imageFile);
}
Thank you already in advanced. I only included the Methods that are responsible for reading and writing the bmp because in the future I want to add some other features.
#tshiono Thank you I added you lines into it but if I compare the hex files I can see that the image still has some differences:
The output image looks still the same.
My Main function looks like this:
int main(int argc, char **argv) {
int ch;
char *inputFile = "sample.bmp";
char *outputFile = "image-copy.bmp";
Image image;
// try to use port and password from parameter
while ( (ch = getopt_long_only(argc, argv, "", long_options, NULL)) != -1 ) {
switch (ch) {
case 'i':
inputFile = optarg;
break;
case 'o':
outputFile = optarg;
break;
default:
break;
}
}
printf("input File: %s\n", inputFile);
readImage(inputFile, &image);
writeImage(outputFile, &image);
return 0;
}
Thank you for the update.
IMHO the bmp file format is stupidly designed which includes some
pitfalls causing unexpected behaviors.
You may have found if we put the 2-byte MagicNumber "BM" in the header struct,
most compilers automatically put another 2 bytes for boundary alignment.
Another pitfall is we need to keep the byte count
of a row (line) to be the multiple of four for word alignment. Then the
starting addresses of every row is located at the word boundary, although
I'm not sure how much it makes the program run faster.
Anyway if the byte count of the row is NOT the multiple of four
(meaning the width is NOT the multiple of four), we need to put extra
dummy bytes for the adjustment at the end of every row. Then would you
please modify your reading/writing functions as:
readImage:
// Read in each pixel
int padding = image->metadata.width % 4; // add this line
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
ImageColors px;
if( fread(&px, sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while reading bmp pixel.\n");
return;
}
image->pixels[i][j] = px;
}
for (int j = 0; j < padding; j++) getc(imageFile); // add this line
}
writeImage:
// Read in each pixel
int padding = image->metadata.width % 4; // add this line
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
if( fwrite(&image->pixels[i][j], sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while wr bmp pixel.\n");
fclose(imageFile);
exit(EXIT_FAILURE);
}
}
for (int j = 0; j < padding; j++) putc(0, imageFile); // add this line
}
where the variable padding is the count of byte stuffing which can be
calculated based on the value of width.
[Update]
Try to add the line:
fseek(imageFile, image->header.offset, SEEK_SET);
just before the line:
// Read in each pixel
in the readImage() as that in writeImage().
[Explanation]
There are several versions in bmp file format and they have different
header size (which is yet another pitfall):
version header.offset (starting address of pixel data)
BMP v2 54
BMP v3 122
BMP v4 138
While your definitions of structs are based on BMP v2, the provided
bmp file is created in BMP v4. Then it causes the conflicts between
header data and the position of the pixel data. That is why your file
comparison shows the difference.
The viewability of the
collapsed image file will depend on the viewer software. In my environment
there is no problem to view the images.
Ideally we should prepare several types of header structs and use one of them
depending on the version. Practically the extended area contains
less significant data and we may skip them in most cases.
I want to convert bytes that is stored in memory like
// pix is array of bytes with format format R, G, B, A, R, G, B, A, ...
// further image is going to be 250x300, so size is 250*300*4
char pix[250*300*4] = {
0, 255, 0, 0, //1
0, 255, 0, 0, //2
...
0, 255, 0, 0 //250*300
} // green image
to png image with help of libpng.
But I have not found any suitable function to do this. So, I am seeking for function like
png_bitmap_to_png(void *bitmap, void* png_raw_bytes).
If you have other ideas how to convert byte array to png image I am glad to hear them but do not offer usage of other libraries or converters like ImageMagick if it is not necessary, please.
I figured out how to do the thing. Instead of pix array I used row_pointers to store my color numbers, you may create some function to convert pix array to row_pointers if you want. Here is my code, I hope it will help
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <png.h>
#define IMAGE_HEIGHT 250
#define IMAGE_WIDTH 300
#define IMAGE_RED_COLOR 0
#define IMAGE_GREEN_COLOR 255
#define IMAGE_BLUE_COLOR 0
#define IMAGE_ALPHA_CHANNEL 255
void write_png_image(char* filename)
{
png_byte** row_pointers; // pointer to image bytes
FILE* fp; // file for image
do // one time do-while to properly free memory and close file after error
{
row_pointers = (png_byte**)malloc(sizeof(png_byte*) * IMAGE_HEIGHT);
if (!row_pointers)
{
printf("Allocation failed\n");
break;
}
for (int i = 0; i < IMAGE_HEIGHT; i++)
{
row_pointers[i] = (png_byte*)malloc(4*IMAGE_WIDTH);
if (!row_pointers[i])
{
printf("Allocation failed\n");
break;
}
}
// fill image with color
for (int y = 0; y < IMAGE_HEIGHT; y++)
{
for (int x = 0; x < IMAGE_WIDTH*4; x+=4)
{
row_pointers[y][x] = IMAGE_RED_COLOR; //r
row_pointers[y][x + 1] = IMAGE_GREEN_COLOR; //g
row_pointers[y][x + 2] = IMAGE_BLUE_COLOR; //b
row_pointers[y][x + 3] = IMAGE_ALPHA_CHANNEL; //a
}
}
//printf("%d %d %d %d\n", row_pointers[0][0], row_pointers[0][1], row_pointers[0][2], row_pointers[0][3]);
fp = fopen(filename, "wb"); //create file for output
if (!fp)
{
printf("Open file failed\n");
break;
}
png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //create structure for write
if (!png)
{
printf("Create write struct failed\n");
break;
}
png_infop info = png_create_info_struct(png); // create info structure
if (!info)
{
printf("Create info struct failed\n");
break;
}
if (setjmp(png_jmpbuf(png))) // this is some routine for errors?
{
printf("setjmp failed\n");
}
png_init_io(png, fp); //initialize file output
png_set_IHDR( //set image properties
png, //pointer to png_struct
info, //pointer to info_struct
IMAGE_WIDTH, //image width
IMAGE_HEIGHT, //image height
8, //color depth
PNG_COLOR_TYPE_RGBA, //color type
PNG_INTERLACE_NONE, //interlace type
PNG_COMPRESSION_TYPE_DEFAULT, //compression type
PNG_FILTER_TYPE_DEFAULT //filter type
);
png_write_info(png, info); //write png image information to file
png_write_image(png, row_pointers); //the thing we gathered here for
png_write_end(png, NULL);
printf("Image was created successfully\nCheck %s file\n", filename);
} while(0);
//close file
if (fp)
{
fclose(fp);
}
//free allocated memory
for (int i = 0; i < IMAGE_HEIGHT; i++)
{
if (row_pointers[i])
{
free(row_pointers[i]);
}
}
if (row_pointers)
{
free(row_pointers);
}
}
int main(int argc, char* argv[])
{
if(argc == 2)
{
write_png_image(argv[1]);
}
else
{
printf("Usage: %s pngfile.png\n", argv[0]);
}
return 0;
}
The following code is part of my program that tries to extract image features from a bitmap file. I need to extract information (width and height only) about the image and also make copies of it. These images are of 2048X 2168 resolution, 8-bit greyscale.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAMELENGTH 301
long getImageInfo(FILE* Finput, long offset, int nchars){
unsigned char *ptrChar;
unsigned char dummy;
long value=0L;
int i;
dummy='0';
ptrChar=&dummy;
fseek(Finput,offset,SEEK_SET);
for (i=1; i<=nchars; i++){
fread(ptrChar,sizeof(char),1,Finput);
value += ((long) ((*ptrChar)*pow(256,(i-1))));
}
return(value);
}
void copyImageInfo(FILE* Finput, FILE* Foutput){
unsigned char *ptrChar;
unsigned char dummy;
long offset;
int i;
dummy='0';
ptrChar=&dummy;
int byte_size = ((int) getImageInfo(Finput,34,4));
/* copying header: signature, image width & height, number bit/pixel, image size, number of colors */
offset=0L;
fseek(Finput,offset,SEEK_SET);
fseek(Foutput,offset,SEEK_SET);
for (i=0; i<=54; i++){
fread(ptrChar,sizeof(char),1,Finput);
fwrite(ptrChar,sizeof(char),1,Foutput);
}
/* copying pixel data */
/* This part of the code may not be complete */
offset=54L;
fseek(Finput,offset,SEEK_SET);
fseek(Foutput,offset,SEEK_SET);
for (i=0; i<=byte_size; i++){
fread(ptrChar,sizeof(char),1,Finput);
fwrite(ptrChar,sizeof(char),1,Foutput);
}
}
int main(int argc, char *argv[]){
FILE* Finput;
FILE* Foutput;
char input_name[NAMELENGTH];
char output_name[NAMELENGTH];
char job_name[NAMELENGTH];
char history_name[NAMELENGTH];
unsigned char *ptrChar;
int pix_width,pix_height;
int bit_pixel,byte_size,ncolors;
strcpy(input_name,argv[1]); /* first argument contains path to the image file */
strcpy(job_name, argv[1]);
job_name[strlen(job_name)-4]='\0';
sprintf(history_name,"%s%s",job_name,"_hist.txt"); /* history file stores image header information */
if( (Finput=fopen(input_name,"r"))==NULL ){
fprintf(stderr,"\n ERROR: file %s could not be opened for reading\n",input_name);
exit(-1);
}
else{
fseek(Finput,0L,SEEK_END);
if (getImageInfo(Finput,0,2)!=19778) fprintf(stdout,"\n WARNING: wrong BMP signature!\n");
pix_width = ((int) getImageInfo(Finput,18,4));
pix_height = ((int) getImageInfo(Finput,22,4));
bit_pixel = ((int) getImageInfo(Finput,28,2));
byte_size = ((int) getImageInfo(Finput,34,4));
ncolors = ((int) getImageInfo(Finput,46,4));
fprintf(stdout,"\n width pixels=%d",pix_width);
fprintf(stdout,"\n height pixels=%d",pix_height);
fprintf(stdout,"\n bits per pixel=%d",bit_pixel);
fprintf(stdout,"\n image data size=%d",byte_size);
fprintf(stdout,"\n number colors=%d\n",ncolors);
/* history file */
if ( (Foutput=fopen(history_name,"a"))==NULL ){
fprintf(stderr,"\n ERROR: file %s could not be opened for appending\n",history_name);
exit(-1);
}
else{
fprintf(Foutput,"Path to Image: %s ",input_name);
fprintf(Foutput,"\n\t 18 - biWidth - width pixels=%d",pix_width);
fprintf(Foutput,"\n\t 22 - biHeight - height pixels=%d",pix_height);
fprintf(Foutput,"\n\t 28 - biBitCount - bits per pixel=%d",bit_pixel);
fprintf(Foutput,"\n\t 34 - biSizeImage - image data size=%d",byte_size);
fprintf(Foutput,"\n\t 46 - biClrUsed - number colors=%d\n",ncolors);
fclose(Foutput);
}
sprintf(output_name,"%s%s",job_name,"_copy.bmp");
if ( (Foutput=fopen(output_name,"wb"))==NULL ){
fprintf(stderr,"\n ERROR: file %s could not be opened for writing\n",output_name);
exit(-1);
}
else{
copyImageInfo(Finput,Foutput);
fclose(Foutput);
}
}
fclose(Finput);
}
Unfortunately, my original image files are in tif format. So when I convert them, I am unable to preserve some of the header file information. One online tool lets me preserve the bits-per-pixel (8 bit) but then I lose the biSizeImage (which turns to 0). Another conversion tool gets me the size correctly but image bitrate changes to 24. (link1, link2)
An example of my original tif image (temporary_link1) and the corresponding BMP image (temporary_link2)
When I run the above, I am able to copy the header information correctly but not the pixel data. If this can be copied using some other method (like for example by comparing the EOF), it may be a good alternative. I am unsure about calculating the padding and also the direction of writing.
Any guidance would be appreciated starting with the correct conversion of tif format to my required bmp format. Evidently I am new to image formatting and compression.
Output:
width pixels=2048
height pixels=2168
bits per pixel=8
image data size=0
number colors=0
getImageInfo is incorrect. The integer values are supposed to be saved in little-endian format. It should be read as follows:
unsigned int getImageInfo(FILE *fin, long offset, int nchars)
{
fseek(fin, offset, SEEK_SET);
unsigned int value = 0;
for(int i = 0; i < nchars; i++)
{
unsigned char dummy = '0';
fread((char*)&dummy, sizeof(char), 1, fin);
value += dummy << (8 * i);
}
return value;
}
byte_size is not guaranteed to be set to the correct value. This should be roughly equal to width * height * bit_pixel. Use this formula.
byte_size = ((pix_width * bit_pixel + 31) / 32) * 4 * pix_height;
Moreover, 8-bit image includes color table of size 1024. This table is immediately after 54 byte header, and before the pixel data. Read it as follows:
unsigned int getImageInfo(FILE *fin, long offset, int nchars)
{
fseek(fin, offset, SEEK_SET);
unsigned int value = 0;
for(int i = 0; i < nchars; i++)
{
unsigned char dummy = '0';
fread((char*)&dummy, sizeof(char), 1, fin);
value += dummy << (8 * i);
}
return value;
}
void copyImageInfo(FILE* Finput, FILE* Foutput)
{
int w = getImageInfo(Finput, 18, 4);
int h = getImageInfo(Finput, 22, 4);
int bit_pixel = getImageInfo(Finput, 28, 2);
int byte_size = ((w * bit_pixel + 31) / 32) * 4 * h;
int ncolors = getImageInfo(Finput, 46, 4);
fprintf(stdout, "\n width pixels=%d", w);
fprintf(stdout, "\n height pixels=%d", h);
fprintf(stdout, "\n bits per pixel=%d", bit_pixel);
fprintf(stdout, "\n image data size=%d", byte_size);
fprintf(stdout, "\n number colors=%d\n", ncolors);
char header[54]; //bitmap header
char *pixels = malloc(byte_size); //pixel data
fseek(Finput, 0, SEEK_SET);
fseek(Foutput, 0, SEEK_SET);
fread(header, sizeof(header), 1, Finput);
fwrite(header, sizeof(header), 1, Foutput);
if(bit_pixel <= 8)
{
//color table
int colors_size = 4 * (1 << bit_pixel);
char *colors = malloc(colors_size);
fread(colors, 1, colors_size, Finput);
fwrite(colors, 1, colors_size, Foutput);
free(colors);
}
fread(pixels, 1, byte_size, Finput);
fwrite(pixels, 1, byte_size, Foutput);
free(pixels);
}
int main(void)
{
char input_name[] = "input.bmp";
char output_name[] = "output.bmp";
FILE *Finput = fopen(input_name, "rb");
if(!Finput)
{
fprintf(stderr, "\n ERROR: file %s\n", input_name);
exit(-1);
}
FILE *Foutput = fopen(output_name, "wb");
if(!Foutput)
{
fprintf(stderr, "\n ERROR: file %s\n", input_name);
fclose(Finput);
exit(-1);
}
if(getImageInfo(Finput, 0, 2) != 19778)
fprintf(stdout, "\n WARNING: wrong BMP signature!\n");
else
copyImageInfo(Finput, Foutput);
fclose(Foutput);
fclose(Finput);
return 0;
}
Does anyone know about some function that loads a grayscale image into a GSL matrix?
Something like:
gsl_matrix *M;
load_image("./image.jpg", M); // any image extension would also be fine
Something like this works on my PC: allows you to load a grayscale JPG image using libjpeg.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <gsl/gsl_matrix.h>
gsl_matrix *load_jpg_image(const char *pFileName)
{
FILE *pFile;
long jpegSize;
unsigned char *pJpegBytes, *pPixels;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
int status, w, h, numComponents, stride, x, y;
gsl_matrix *pMatrix;
pFile = fopen(pFileName, "rb");
if (!pFile)
{
fprintf(stderr, "Can't open file\n");
return NULL;
}
// Get the size of the file
fseek(pFile, 0, SEEK_END);
jpegSize = ftell(pFile);
rewind(pFile);
if (jpegSize == 0)
{
fclose(pFile);
fprintf(stderr, "Empty file\n");
return NULL;
}
// Read it into memory and close file
pJpegBytes = (unsigned char *)malloc(jpegSize);
fread(pJpegBytes, 1, jpegSize, pFile);
fclose(pFile);
// Jpeg decompression starts here
memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct));
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, pJpegBytes, jpegSize);
status = jpeg_read_header(&cinfo, TRUE);
if (status != 1)
{
free(pJpegBytes);
fprintf(stderr, "Invalid JPEG header\n");
return NULL;
}
jpeg_start_decompress(&cinfo);
w = cinfo.output_width;
h = cinfo.output_height;
numComponents = cinfo.output_components;
if (numComponents != 1)
{
free(pJpegBytes);
fprintf(stderr, "Can only handle 1 color component\n");
return NULL;
}
pPixels = (unsigned char *)malloc(w*h);
stride = w*numComponents;
// perhaps this can de done much faster by processing
// multiple lines at once
while (cinfo.output_scanline < cinfo.output_height)
{
unsigned char *buffer_array[1];
buffer_array[0] = pPixels + cinfo.output_scanline * stride;
jpeg_read_scanlines(&cinfo, buffer_array, 1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(pJpegBytes);
// Now, create and fill in the matrix
pMatrix = gsl_matrix_alloc(h, w);
for (y = 0 ; y < h ; y++)
for (x = 0 ; x < w ; x++)
gsl_matrix_set(pMatrix, y, x, pPixels[x+y*stride]);
return pMatrix;
}
int main(void)
{
gsl_matrix *pMatrix;
int rows, cols;
int i, j;
pMatrix = load_jpg_image("test.jpg");
if (pMatrix == NULL)
{
fprintf(stderr, "Can't load matrix\n");
return -1;
}
//
// Use the matrix
//
gsl_matrix_free(pMatrix);
return 0;
}
I'm looking for a way to decode some png file, I heard about libpng, but I don't understand how this one works. Does it convert the png file into an array of bytes in the ARGB8888 format or something else ?
Runnable example
This example reads and existing PNG, modifies it, and writes a modified version to disk.
The modification part is done on raw bytes.
Usage:
./a.out [<old-png> [<new-png>]]
old-png defaults to a.png
new-png defaults to b.png
Tested on Ubuntu 18.04, libpng 1.6.34, compile with:
gcc -std=c99 main.c -lpng
Adapted from: https://gist.github.com/niw/5963798
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
unsigned int width;
unsigned int height;
png_bytep *row_pointers;
static void read_png_file(char *filename) {
FILE *fp = fopen(filename, "rb");
png_byte bit_depth;
png_byte color_type;
unsigned int y;
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) abort();
png_infop info = png_create_info_struct(png);
if (!info) abort();
if (setjmp(png_jmpbuf(png))) abort();
png_init_io(png, fp);
png_read_info(png, info);
width = png_get_image_width(png, info);
height = png_get_image_height(png, info);
color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info);
/* Read any color_type into 8bit depth, RGBA format. */
/* See http://www.libpng.org/pub/png/libpng-manual.txt */
if (bit_depth == 16)
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
/* PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
/* These color_type don't have an alpha channel then fill it with 0xff. */
if (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for (y = 0; y < height; y++) {
row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info));
}
png_read_image(png, row_pointers);
fclose(fp);
}
static void write_png_file(char *filename) {
unsigned int y;
FILE *fp = fopen(filename, "wb");
if (!fp) abort();
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) abort();
png_infop info = png_create_info_struct(png);
if (!info) abort();
if (setjmp(png_jmpbuf(png))) abort();
png_init_io(png, fp);
png_set_IHDR(
png,
info,
width,
height,
8,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
png_write_info(png, info);
/* To remove the alpha channel for PNG_COLOR_TYPE_RGB format, */
/* Use png_set_filler(). */
/*png_set_filler(png, 0, PNG_FILLER_AFTER);*/
png_write_image(png, row_pointers);
png_write_end(png, NULL);
for (y = 0; y < height; y++) {
free(row_pointers[y]);
}
free(row_pointers);
fclose(fp);
}
static void process_png(void) {
for (unsigned int y = 0; y < height; y++) {
png_bytep row = row_pointers[y];
for (unsigned int x = 0; x < width; x++) {
png_bytep px = &(row[x * 4]);
/*printf("%4d, %4d = RGBA(%3d, %3d, %3d, %3d)\n", x, y, px[0], px[1], px[2], px[3]);*/
png_byte old[4 * sizeof(png_byte)];
memcpy(old, px, sizeof(old));
px[0] = 255 - old[0];
px[1] = 255 - old[1];
px[2] = 255 - old[2];
}
}
}
int main(int argc, char *argv[]) {
char *in;
char *out;
if (argc > 1) {
in = argv[1];
} else {
in = "a.png";
}
if (argc > 2) {
out = argv[2];
} else {
out = "b.png";
}
read_png_file(in);
process_png();
write_png_file(out);
return EXIT_SUCCESS;
}
a.png
b.png
Image source: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Catherine_of_Aragon.png/430px-Catherine_of_Aragon.png
You might be better off looking at a dedicated image library that will decode the image for you and return it in a recognised structure. It'll also serve as a better platform when you want to actually do something with the image (saving, displaying, etc).