How To Read Data From Image File In C#
Solarian Programmer
My programming ramblings
C Programming - Reading and writing images with the stb_image libraries
Posted on June 10, 2019 by Paul
In this article I will show y'all how to read and write images with the stb_image libraries. In order to exemplify the usage of the library I'll demo how to convert an prototype to gray and how to use a sepia filter to the epitome.
As a side note, the C code from this article is compatible with whatever modern C compilers similar GCC, Clang and MSVC.
You can find the source files and paradigm examples used here on the GitHub repo for this article.
The article consists of two parts:
- stb_image basic usage
- Writing a wrapper around the stb_image functions
stb_image basic usage
Let's start by getting the libraries from GitHub:
i git clone https://github.com/nothings/stb.git
if you lot don't take git installed on your computer yous tin use the Download Cypher choice.
Side by side, copy the three files prefixed with stb_image from the stb binder in a new folder named stb_image that, from now on, I will assume information technology is nowadays in your projection folder. Optionally, yous can remove the stb folder from your auto.
At that place is too a video version of this role of the tutorial:
Nosotros'll start with an example of basic usage of the library functions. We'll read an prototype from the disk and write it back. The purpose of this example is to familiarize you with the library interface.
We demand to define STB_IMAGE_IMPLEMENTATION earlier including the stb_image.h header. This needs to exist defined just once. If you need to include the stb_image header in another C source file don't redefine the STB_IMAGE_IMPLEMENTATION. Same considerations apply to defining STB_IMAGE_WRITE_IMPLEMENTATION earlier including the "stb_image_write" header file:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define STB_IMAGE_IMPLEMENTATION 5 #include "stb_image/stb_image.h" six #define STB_IMAGE_WRITE_IMPLEMENTATION 7 #include "stb_image/stb_image_write.h" eight 9 int master ( void ) { 10 // ... 11 }
In lodge to read an prototype from the disk we'll use the stbi_load function that receives as arguments the epitome file path, width and summit of the epitome, the number of color channels and the desired number of colour channels. The last argument of the function is useful if for example you want to load merely the R, G, B channels from a 4 channel, PNG, image and ignore the transparency channel. If y'all want to load the image equally is, pass 0 as the terminal parameter of the load function. In case of error, the stbi_load part returns NULL.
ane // ... ii 3 int main ( void ) { 4 int width , height , channels ; five unsigned char * img = stbi_load ( "heaven.jpg" , & width , & height , & channels , 0 ); 6 if ( img == Zero ) { seven printf ( "Error in loading the prototype \n " ); 8 exit ( i ); 9 } 10 printf ( "Loaded image with a width of %dpx, a height of %dpx and %d channels \n " , width , height , channels ); xi 12 // ... xiii }
After yous've finished processing the image, y'all tin write it back to the disk using one of the stbi_image_write functions. Here I'll testify you how to relieve the above loaded image as PNG and JPG images:
ane // ... ii 3 int main ( void ) { 4 // ... 5 6 stbi_write_png ( "heaven.png" , width , summit , channels , img , width * channels ); seven stbi_write_jpg ( "sky2.jpg" , width , peak , channels , img , 100 ); 8 9 stbi_image_free ( img ); ten }
The 6th parameter of the stbi_write_png office from the above code is the image stride, which is the size in bytes of a row of the image. The last parameter of the stbi_write_jpg function is a quality parameter that goes from ane to 100. Since JPG is a lossy image format, you tin can chose how much data is dropped at salve time. Lower quality means smaller image size on disk and lower visual epitome quality. Most image editors, like GIMP, will relieve jpg images with a default quality parameter of 80 or 90, but the user can melody the quality parameter if required. I've used a quality parameter of 100 in all examples from this commodity.
Once you are done with an image, you tin can release the retention used to store its information with the stbi_image_free role.
Yous can try the in a higher place code by building and running t0.c from the commodity repo.
Every bit mentioned before, you can load merely the first channels of an image. Here is an instance of loading the first iii color channels of a PNG paradigm:
1 // ... 2 3 int main ( void ) { iv int width , height , original_no_channels ; 5 int desired_no_channels = iii ; 6 unsigned char * img = stbi_load ( "Shapes.png" , & width , & tiptop , & original_no_channels , desired_no_channels ); vii if ( img == NULL ) { 8 printf ( "Fault in loading the epitome \n " ); 9 go out ( one ); 10 } 11 printf ( "Loaded paradigm with a width of %dpx, a height of %dpx. The original epitome had %d channels, the loaded image has %d channels. \n " , width , summit , original_no_channels , desired_no_channels ); 12 13 // ... 14 }
Observation, if yous make up one's mind to load merely a sure number of channels from an paradigm, be conscientious to use the desired number of channels in further operations on the paradigm data. The fourth argument of the stbi_load function volition be initialized with the original number of channels of the image. For example, if you desire to salvage the above loaded image yous will use something like this:
1 // ... 2 3 int master ( void ) { 4 // ... 5 vi stbi_write_png ( "1.png" , width , height , desired_no_channels , img , width * desired_no_channels ); seven eight // ... nine }
You tin see a complete instance of partially loading an epitome in t01.c from this article repo.
Another observation, if y'all are interested only in the image full general information, similar the image size and number of channels, you lot can avoid loading the paradigm in memory by using the stbi_info function which will initialize the width, height and number of channels parameters. Hither is a snippet example:
one // ... 2 iii int main ( void ) { iv int width , height , channels ; 5 const char * fname = "Shapes.png" ; vi stbi_info ( fname , & width , & height , & channels ); 7 8 // ... 9 }
As a kickoff example of prototype manipulation, I will show yous how to convert the loaded prototype to gray and save information technology to the deejay. The purpose of this example is to evidence you how to loop over an epitome data and how to access and modify the pixel values.
Permit's assume that you lot've successfully loaded a PNG or JPG epitome and that you want to convert this to gray. The output image will have two or ane channels. For instance, if the input image has a transparency channel this will exist simply copied to the second channel of the gray image, while the first channel of the gray image will contain the grey pixel values. If the input image has three channels, the output image will accept just one channel with the gray data.
Here is a possible implementation of setting the number of channels and allocating memory for the gray prototype:
one // ... ii 3 int main ( void ) { 4 // Load an image 5 // ... 6 7 // Convert the input prototype to gray 8 size_t img_size = width * height * channels ; ix int gray_channels = channels == iv ? 2 : one ; x size_t gray_img_size = width * height * gray_channels ; 11 12 unsigned char * gray_img = malloc ( gray_img_size ); xiii if ( gray_img == NULL ) { xiv printf ( "Unable to allocate memory for the gray image. \n " ); 15 exit ( 1 ); 16 } 17 xviii // ... 19 }
Next, we'll loop over the pixels of the input prototype, calculate the greyness value as the boilerplate of the blood-red, light-green and blue channels and store the grey value in the output epitome. If the input image has a transparency channel, we'll re-create the values of this to the second channel of the output grayness image:
1 // ... ii 3 int main ( void ) { 4 // Load an image five // ... 6 seven // Catechumen the input image to grayness 8 // Allocate memory for the output image 9 // ... 10 11 for ( unsigned char * p = img , * pg = gray_img ; p != img + img_size ; p += channels , pg += gray_channels ) { 12 * pg = ( uint8_t )(( * p + * ( p + ane ) + * ( p + 2 )) / 3.0 ); 13 if ( channels == iv ) { xiv * ( pg + 1 ) = * ( p + three ); fifteen } 16 } 17 18 // ... nineteen }
In the above code the p arrow volition go over the input image, while the pg arrow will go over the output prototype.
Once the gray image is filled, y'all can save it every bit before, due east.m.:
ane // ... ii three int master ( void ) { 4 // Load an epitome and convert it to gray 5 // ... half-dozen seven stbi_write_jpg ( "sky_gray.jpg" , width , tiptop , gray_channels , gray_img , 100 ); 8 nine // ... 10 }
Next, I volition show you how to convert a color image to sepia. In this case the output image volition have the same size in bytes as the input image. The color channels of the sepia paradigm are a mix of the color channels of the input image:
ane // ... 2 three int chief ( void ) { four // Load an epitome 5 // ... 6 7 // Convert the input image to sepia 8 unsigned char * sepia_img = malloc ( img_size ); ix if ( sepia_img == Zilch ) { 10 printf ( "Unable to allocate memory for the sepia image. \northward " ); 11 leave ( 1 ); 12 } xiii 14 // Sepia filter coefficients from https://stackoverflow.com/questions/1061093/how-is-a-sepia-tone-created 15 for ( unsigned char * p = img , * pg = sepia_img ; p != img + img_size ; p += channels , pg += channels ) { 16 * pg = ( uint8_t ) fmin ( 0.393 * * p + 0.769 * * ( p + 1 ) + 0.189 * * ( p + ii ), 255.0 ); // red 17 * ( pg + one ) = ( uint8_t ) fmin ( 0.349 * * p + 0.686 * * ( p + i ) + 0.168 * * ( p + two ), 255.0 ); // light-green 18 * ( pg + ii ) = ( uint8_t ) fmin ( 0.272 * * p + 0.534 * * ( p + i ) + 0.131 * * ( p + ii ), 255.0 ); // blue 19 if ( channels == four ) { 20 * ( pg + iii ) = * ( p + 3 ); 21 } 22 } 23 24 stbi_write_jpg ( "sky_sepia.jpg" , width , height , channels , sepia_img , 100 ); 25 26 27 // ... 28 }
Y'all tin notice a complete example of loading an image and converting it to gray and sepia as t1.c in the repo for this article.
Writing a wrapper around the stb_image functions
In this part of the article we are going to abstract the lawmaking presented before to a small-scale prototype library. The reward of using a small abstraction over directly calling the stb_image functions is that nosotros can put all image related data in a structure and write some utility functions that manipulate the Prototype struct. Nosotros can also reduce the possibility of user errors by presenting a simpler interface.
There is likewise a video version of this role of the tutorial:
We'll start by writing the Image header file which comprise the public interface for our small library:
1 #pragma once 2 three #include <stdlib.h> 4 #include <stdint.h> five #include <stdbool.h> 6 seven enum allocation_type { eight NO_ALLOCATION , SELF_ALLOCATED , STB_ALLOCATED ix }; 10 11 typedef struct { 12 int width ; xiii int height ; 14 int channels ; 15 size_t size ; 16 uint8_t * data ; 17 enum allocation_type allocation_ ; 18 } Epitome ; nineteen 20 void Image_load ( Image * img , const char * fname ); 21 void Image_create ( Image * img , int width , int height , int channels , bool zeroed ); 22 void Image_save ( const Prototype * img , const char * fname ); 23 void Image_free ( Image * img ); 24 void Image_to_gray ( const Paradigm * orig , Epitome * gray ); 25 void Image_to_sepia ( const Epitome * orig , Epitome * sepia );
The Image struct from the in a higher place header file is self explanatory, you can find information technology every bit Prototype.h in the article repo. The last field of the Image struct, the allocation blazon enumeration, is used to record if the memory was allocated past the user or by one of the stb_image functions.
Adjacent, we accept utility functions to load, create, relieve and free an image. For simplicity, nosotros've assumed that the user will relieve to disk just PNG or JPG images. The code can be easily extended with other output functions from the stb_image_write or with new functions written by the user.
The terminal two functions from Image.h are used to convert an input epitome to gray or sepia. Nosotros basically, have the code written in t1.c and put it in split functions. These two functions volition also allocate the necessary memory for the output image.
The actual implementation of the higher up functions is in Image.c. The implementation code is basically a modified version of the code from t1.c, so information technology won't exist presented in the article.
Hither is an example of using the above library to load two input images: sky.jpg and Shapes.png, convert the images to gray and sepia, save the output images and free the retention used for storing the input and output images:
1 #include "Image.h" 2 #include "utils.h" 3 4 int main ( void ) { 5 Paradigm img_sky , img_shapes ; vi 7 Image_load ( & img_sky , "heaven.jpg" ); 8 ON_ERROR_EXIT ( img_sky . data == Naught , "Fault in loading the paradigm" ); ix Image_load ( & img_shapes , "Shapes.png" ); 10 ON_ERROR_EXIT ( img_shapes . data == NULL , "Error in loading the image" ); 11 12 // Convert the images to gray 13 Paradigm img_sky_gray , img_shapes_gray ; 14 Image_to_gray ( & img_sky , & img_sky_gray ); 15 Image_to_gray ( & img_shapes , & img_shapes_gray ); 16 17 // Convert the images to sepia 18 Image img_sky_sepia , img_shapes_sepia ; nineteen Image_to_sepia ( & img_sky , & img_sky_sepia ); twenty Image_to_sepia ( & img_shapes , & img_shapes_sepia ); 21 22 // Save images 23 Image_save ( & img_sky_gray , "sky_gray.jpg" ); 24 Image_save ( & img_sky_sepia , "sky_sepia.jpg" ); 25 Image_save ( & img_shapes_gray , "Shapes_gray.png" ); 26 Image_save ( & img_shapes_sepia , "Shapes_sepia.png" ); 27 28 // Release memory 29 Image_free ( & img_sky ); 30 Image_free ( & img_sky_gray ); 31 Image_free ( & img_sky_sepia ); 32 33 Image_free ( & img_shapes ); 34 Image_free ( & img_shapes_gray ); 35 Image_free ( & img_shapes_sepia ); 36 }
A special mention virtually utils.h used in the to a higher place code, this header file contains an error checking helper macro that, in case of error, will print the calling role and line number were the error was detected. You can observe the above lawmaking every bit t2.c in the commodity repo.
If you lot want to learn more than nigh C99/C11 I would recommend reading 21st Century C: C Tips from the New Schoolhouse by Ben Klemens:
or the classic C Bible, The C Programming Linguistic communication by B.Westward. Kernighan, D.M. Ritchie:
How To Read Data From Image File In C#,
Source: https://solarianprogrammer.com/2019/06/10/c-programming-reading-writing-images-stb_image-libraries/
Posted by: bustillosclaill1953.blogspot.com
0 Response to "How To Read Data From Image File In C#"
Post a Comment