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.
Related
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;
}
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;
}
I'm trying to compute the average vector in RGB tristimulus space with libpng in C, and NumPy in Python, but I'm getting different results with each. I'm quite confident Python is giving the correct result with this image of [ 127.5 127.5 0. ]. However, with the following block of C I get the preposterous result of [ 38.406494 38.433670 38.459641 ]. I've been staring at my code for weeks without any give, so I thought I'd see if others may have an idea.
Also, I've tested this code with other images and it gives similar preposterous results. It's quite curious because all three numbers usually match for the first 4 or so digits. I'm not sure what may be causing this.
/* See if our average vector matches that of Python's */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <png.h>
// For getting the PNG data and header/information back
typedef struct
{
uint32_t width; // width of image
uint32_t height; // height of image
int bit_depth; // bits/pixel component (should be 8 in RGB)
png_bytep datap; // data
} rTuple;
#define PNG_BYTES_TO_CHECK 8
#define CHANNELS 3
int
check_PNG_signature(unsigned char *buffer)
{
unsigned i;
const unsigned char signature[8] = { 0x89, 0x50, 0x4e, 0x47,
0x0d, 0x0a, 0x1a, 0x0a };
for (i = 0; i < PNG_BYTES_TO_CHECK; ++i)
{
if (buffer[i] != signature[i])
{
fprintf(stderr, "** File sig does not match PNG, received ");
for (i = 0; i < PNG_BYTES_TO_CHECK; ++i)
fprintf(stderr, "%.2X ", buffer[i]);
fprintf(stderr, "\n");
abort();
}
}
return 1;
}
rTuple
read_png_file(char *file_name)
{
/* Get PNG data - I've pieced this together by reading `example.c` from
beginning to end */
printf("** Reading data from %s\n", file_name);
png_uint_32 width, height; // holds width and height of image
uint32_t row; // for iteration later
int bit_depth, color_type, interlace_type;
unsigned char *buff = malloc(PNG_BYTES_TO_CHECK * sizeof(char));
memset(buff, 0, PNG_BYTES_TO_CHECK * sizeof(char));
FILE *fp = fopen(file_name, "rb");
if (fp == NULL) abort();
if (fread(buff, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) {
fprintf(stderr, "** Could not read %d bytes\n", PNG_BYTES_TO_CHECK);
abort();
}
check_PNG_signature(buff);
rewind(fp);
// create and initialize the png_struct, which will be destroyed later
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING
, NULL /* Following 3 mean use stderr & longjump method */
, NULL
, NULL
);
if (!png_ptr) abort();
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) abort();
// following I/O initialization method is required
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 0); // libpng has this built in too
// call to png_read_info() gives us all of the information from the
// PNG file before the first IDAT (image data chunk)
png_read_info(png_ptr, info_ptr);
// Get header metadata now
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, NULL, NULL);
// Scale 16-bit images to 8-bits as accurately as possible (shouldn't be an
// issue though, since we're working with RGB data)
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
png_set_scale_16(png_ptr);
#else
png_set_strip_16(png_ptr);
#endif
png_set_packing(png_ptr);
// PNGs we're working with should have a color_type RGB
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
// Required since we selected the RGB palette
png_read_update_info(png_ptr, info_ptr);
// Allocate memory to _hold_ the image data now (lines 547-)
png_bytep row_pointers[height];
for (row = 0; row < height; ++row)
row_pointers[row] = NULL;
for (row = 0; row < height; ++row)
row_pointers[row] = png_malloc(png_ptr,\
png_get_rowbytes(png_ptr, info_ptr)
);
png_read_image(png_ptr, row_pointers);
png_read_end(png_ptr, info_ptr);
// Now clean up - the image data is in memory
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
rTuple t = { width, height, bit_depth, *row_pointers };
return t;
}
int
main(int argc, char *argv[])
{
if (argc != 2) {
printf("** Provide filename\n");
abort();
}
char *fileName = argv[1];
// get data read
rTuple data = read_png_file(fileName);
/* let's try computing the absolute average vector */
uint32_t i, j, k;
double *avV = malloc(CHANNELS * sizeof(double));
memset(avV, 0, sizeof(double) * CHANNELS);
double new_px[CHANNELS];
png_bytep row, px;
for (i = 0; i < data.height; ++i)
{
row = &data.datap[i];
for (j = 0; j < data.width; ++j)
{
px = &(row[j * sizeof(int)]);
for (k = 0; k < CHANNELS; ++k) {
new_px[k] = (double)px[k];
avV[k] += new_px[k];
}
}
}
double size = (double)data.width * (double)data.height;
for (k = 0; k < CHANNELS; ++k) {
avV[k] /= size;
printf("channel %d: %lf\n", k + 1, avV[k]);
}
printf("\n");
return 0;
}
Now with Python I'm just opening an image with a simple context manager and computing np.mean(image_data, axis=(0, 1)), which yields my result above.
Basically, you had a couple bugs (libpng side and pointer arithmetic) that I try to find them by comparing your code with this Github gist. The followings are the list of changes that I have made to produce the same image mean as Python NumPy.
In rTuple struct, you need to change the png_bytep datap to a pointer of type png_byte using: png_bytep *datap;.
In read_png_file, use png_set_filler to add the filler byte after reading the image. See here for more information about it.
if(color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
In read_png_file, update the changes before allocating the row_pointers using png_read_update_info(png_ptr, info_ptr);
Again, in read_png_file, change the way you are mallocing memory for image pixels using:
png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for(row = 0; row < height; row++)
{
row_pointers[row] = malloc(png_get_rowbytes(png_ptr,info_ptr));
}
In main, change row = &data.datap[i]; to row = data.datap[i]; as your accessing a pointer here.
I did not want to populate the answer with a code that is barely the same as the question, so if you want to just copy and paste the answer, this is the link to the complete code.
This is my function, I am using the headers BMP according to wikipedia BITMAPINFOHEADER. But, I am getting a file without any image...when putting padding, the process stops.
// Structures for header info
#pragma pack(push,1)
/* Windows 3.x bitmap file header */
typedef struct {
char filetype[2]; /* magic - always 'B' 'M' */
unsigned int filesize;
short reserved1;
short reserved2;
unsigned int dataoffset; /* offset in bytes to actual bitmap data */
} file_header;
/* Windows 3.x bitmap full header, including file header */
typedef struct {
file_header fileheader;
unsigned int headersize;
int width;
int height;
short planes;
short bitsperpixel; /* we only support the value 24 here */
unsigned int compression; /* we do not support compression */
unsigned int bitmapsize;
int horizontalres;
int verticalres;
unsigned int numcolors;
unsigned int importantcolors;
} bitmap_header;
#pragma pack(pop)
int RGB2GREY(char* input, char *greyImage) {
//variable declaration:
FILE *fp, *grey;
bitmap_header* hp;
int n;
char *data;
int oldBitsperpixel;
//Open input file:
fp = fopen(input, "rb");
if(fp==NULL){
//cleanup
}
//Read the input file headers:
hp=(bitmap_header*)malloc(sizeof(bitmap_header));
if(hp==NULL)
return 3;
n=fread(hp, sizeof(bitmap_header), 1, fp);
if(n<1){
//cleanup
}
//Read the data of the image:
data = (char*)malloc(sizeof(char)*hp->bitmapsize);
if(data==NULL){
//cleanup
}
//Put me in the position after header...
fseek(fp,sizeof(char)*hp->fileheader.dataoffset,SEEK_SET);
printf("Width %d and Height %d\n",hp->width,hp->height);
int i, j;
unsigned char BGR[3];
unsigned colorIntensity[3];
/*unsigned char bmppad[hp->width] = {0};*/
printf("New bitmapSize %d\n\n",hp->bitsperpixel);
//Open greayImage file:
grey = fopen(greyImage, "wb");
if(grey==NULL){
//cleanup
}
//Writes the header
n=fwrite(hp,sizeof(char),sizeof(bitmap_header),grey);
if(n<1){
//cleanup
}
//Again going to position after header
fseek(out,sizeof(char)*hp->fileheader.dataoffset,SEEK_SET);
for (i=0; i<hp->height; i++){
for (j=0; j<hp->width; j++){
//Reading pixel by pixel
fread(BGR, 3, 1, fp); //1 unsigned char of 3 positions
unsigned char colorGrey;
colorGrey = (unsigned char) 0.3*BGR[2] + 0.6*BGR[1] + 0.1*BGR[0];
colorIntensity[2] = colorGrey;
colorIntensity[1] = colorGrey;
colorIntensity[0] = colorGrey;
/*printf("B %d G %d R %d ",BGR[0],BGR[1],BGR[2]);
printf("Gray %d ",colorIntensity);*/
fwrite(colorIntensity, 3, 1, grey);
}
/*
// Adding pad option1
//fwrite(bmppad, sizeof(bmppad), 1, grey);
//Adding pad option2
for (j=0; j>hp->width; j++){
fwrite(0, 1, 1, grey);
}*/
}
fclose(fp);
fclose(grey);
free(hp);
free(data);
return 0;
}
In the grey output file, I get nothing...moreover, I wonder if there is a way to reduce from 24 to 8 bits.
ps. My code came from reading/writing bmp files in c
The formula came from Create greyscale BMP from RGB BMP
Thanks,
You are essentially turning a 32-bit color bitmap into a 32-bit grey bitmap by changing the color values in a way that they appear grey (you are not saving any space in this way; the bitmap stays as large as it was). Anayway, it explains why you do not need to adapt the bitmap header.
But when you read every three bytes and change every three bytes, you do not take scanlines into account.
An image consists of scanlines and a scanline consits of pixels. Scanlines are alligned on even word boundaries so the last few bytes of a scanline are unused (and the scanline is thus a bit longer than all the pixels on it).
To properly process the input and create the output, your loop must be:
(EDIT: updated to use 1 byte per pixel output):
#pragma pack(push,1)
typedef struct {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} pal_entry;
#pragma pack(pop)
int ToGreyScale(FILE *fp, FILE *grey, bitmap_header *hp)
{
int i, j;
int iScanlineSizeIn = ((hp->width * hp->bitsperpixel) + 31) / 32 * 4;
int iScanlineSizeOut= ((hp->width * 8 ) + 31) / 32 * 4;
unsigned char *scanlineIn = malloc(iScanlineSizeIn), *pIn;
unsigned char *scanlineOut= malloc(iScanlineSizeOut), *pOut;
pal_entry pal[256];
for (i=0; i<256; i++) // create a gray scale palette
{pal[i].rgbBlue= i; pal[i].rgbGreen= i; pal[i].rgbRed= i;}
hp->bitsperpixel= 8; // set output bits-per-pixel
hp->fileheader.filesize= sizeof(bitmap_header) + sizeof(pal) + hp->width*iScanlineSizeOut;
fwrite(hp, sizeof(bitmap_header), 1, grey); // write the header...
fwrite(pal, 256*sizeof(pal_entry), 1, grey); //..followed by palette
for (i=0; i<hp->height; i++)
{
if (fread(scanlineIn, iScanlineSizeIn, 1, fp) != 1) return(0);
pIn = scanlineIn;
pOut= scanlineOut;
for (j=0; j<hp->width; j++)
{
*pOut++ = (unsigned char) ((0.1 * *pIn++) + (0.6 * *pIn++) + (0.3 * *pIn++));
}
fwrite(scanlineOut, iScanlineSizeOut, 1, grey);
}
free(scanlineIn);
free(scanlineOut);
return(1);
}
I am currently facing issues when trying to apply the run-length algorithm to a .bmp picture. When I am doing the encoding, all is working well, but at decoding there is a problem and I cannot figure it out, namely I cannot read the number of repetitive pixels and thus my output is a blank image. When I use the debugger, the number of repetitive pixels is not readed correctly from the binary file (the result of applying RLE on the bitmap file). Here is my code
#include<stdlib.h>
#include <stdint.h>
//structure defiens bitmap header
struct BITMAPFILEHEADER{
uint8_t type[2];//type of file (bit map)
uint32_t size;//size of file
uint16_t reserved1;//
uint16_t reserved2;//
uint32_t offsetbits;//off set bits
} __attribute__ ((packed));
struct BITMAPINFOHEADER{
uint32_t size;//bitmap size
// uint16_t w2;
uint32_t width;//width of bitmap
//uint16_t h2;
uint32_t height;//hight of bitmap
uint16_t planes;
uint16_t bitcount;
uint32_t compression;// compression ratio (zero for no compression)
uint32_t sizeimage;//size of image
long xpelspermeter;
long ypelspermeter;
uint32_t colorsused;
uint32_t colorsimportant;
} __attribute__ ((packed));
//const char* INPUT_FILE = "/home/bogdan/bee.bmp";
const char* INPUT_FILE = "/home/bogdan/Linux.bmp";
const char* ENCODED_FILE = "/home/bogdan/encoded.bin";
const char* DECODED_FILE = "/home/bogdan/decoded.bmp";
typedef struct SINGLE_PIXEL{
uint8_t green;//Green level 0-255
uint8_t red; //Red level 0-255
} PIXEL;
int comparePixels(PIXEL, PIXEL);
void encode();
void decode(char*);
int main()
{
encode();
decode(ENCODED_FILE);
return 0;
}
void encode() {
uint32_t i=0;//to count pixels read
uint32_t pixno=0;//number of pixels to read
struct BITMAPFILEHEADER source_head;//to store file header
struct BITMAPINFOHEADER source_info;//to store bitmap info header
PIXEL pixel;// the current pixel
FILE *in;// bitmap imput pointer file
FILE *out;//output file pointer
if(!(in=fopen(INPUT_FILE,"rb")))//open in binary read mode
{
printf("\ncan not open file");//error at opening file
exit(-1);
}
out=fopen(ENCODED_FILE,"wb");//opne in binary write mode
//read the headers to source file
fread(&source_head,sizeof(struct BITMAPFILEHEADER),1,in);
fread(&source_info,sizeof(struct BITMAPINFOHEADER),1,in);
//write the headers to the output file
fwrite(&source_head,sizeof(struct BITMAPFILEHEADER),1,out);
fwrite(&source_info,sizeof(struct BITMAPINFOHEADER),1,out);
//cumpute the number of pixels to read
pixno=source_info.width*source_info.height;
// init list of pixels
PIXEL pixArr[pixno];
printf("total pixels: %d", pixno);
//printf("w:%f h:%u pn:%lu", (source_head.size/1024.0/1024), source_info.height, pixno);
uint32_t sum = 0;
//read, modify and write pixels
for(i=0;i<pixno;++i)
{
//read pixel form source file
fread(&pixel,sizeof(PIXEL),1,in);
pixArr[i] = pixel;
}
for (i = 0; i < pixno; i++) {
// printf ("i = %d\tred = %d green = %d blue = %d\n",i, pixArr[i].red, pixArr[i].green, pixArr[i].blue);
int runlength = 1;
while ((i + 1) < pixno) {
if (comparePixels(pixArr[i], pixArr[i+1]) == 0){
// printf ("i = %d\t red = %d green = %d blue = %d\n",i, pixArr[i].red, pixArr[i].green, pixArr[i].blue);
runlength++;
i++;
} else {
break;
}
}
//fprintf(out, "%d", runlength);
fwrite(&runlength, sizeof(runlength), 1, out);
fwrite(&pixel,sizeof(PIXEL),1,out);
sum += runlength;
runlength = 0;
}
//write the modification to the output file
//close all fiels
fclose(in);
fclose(out);
printf("sum = %d",sum);
}
void decode(char * filePath) {
uint32_t i=0;//to count pixels read
uint32_t j=0;
uint32_t totalPixels=0;//number of pixels to read
uint32_t pixelRepetition = 1;
struct BITMAPFILEHEADER source_head;//to store file header
struct BITMAPINFOHEADER source_info;//to store bitmap info header
PIXEL pixel;// the current pixel
FILE *in;// bitmap encoded pointer file
FILE *out;//decoded bitmap file pointer
if (!(in = fopen(filePath, "rb"))) {
printf("\ncan not open file");
exit(-1);
}
out = fopen(DECODED_FILE, "wb");
//read the headers to source file
fread(&source_head,sizeof(struct BITMAPFILEHEADER),1,in);
fread(&source_info,sizeof(struct BITMAPINFOHEADER),1,in);
//write the headers to the output file
fwrite(&source_head,sizeof(struct BITMAPFILEHEADER),1,out);
fwrite(&source_info,sizeof(struct BITMAPINFOHEADER),1,out);
totalPixels=source_info.width*source_info.height;
while(i < totalPixels) {
fread(&pixelRepetition, sizeof(pixelRepetition), 1, out);
fread(&pixel,sizeof(PIXEL),1,in);
for (j = 0; j < pixelRepetition; j++) {
fwrite(&pixel,sizeof(PIXEL),1,out);
}
i += pixelRepetition;
}
fclose(in);
fclose(out);
}
int comparePixels(PIXEL px1, PIXEL px2) {
if (px1.red == px2.red && px1.green == px2.green && px1.blue == px2.blue) {
return 0;
} else {
return -1;
}
}
My implementation is working as follow: I read the header of a bmp file and put it directly to another file. After that I compare pixels to see if they have the same RGB values. If this is true (according to comparePixels function) I put the number of consecutive identical pixels and one pixel (the one which is repeated) into the file. This is the encode phase. At decoding, I am reading the header of the image and then I am trying to read the number of repetitions (1 default, means no repetitive pixels) and the pixel which is repeated. I will really appreciate any type of help. Thank you.
You are repeatedly writing out only the very last pixel you read in:
fwrite(&runlength, sizeof(runlength), 1, out);
fwrite(&pixel,sizeof(PIXEL),1,out);
You need to write out your current pixel instead:
fwrite(pixArr[i], sizeof(PIXEL),1,out);