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;
}
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'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'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.
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).