Arizona State University SER334: Operating Systems and System Programming
TA Edgar, ATP Acua Revised 3/17/2023
Image Processing
Summary: In this homework, you will be implementing a program that loads an image le, applies
ltering to it, and saves it back to disk. You will also learn how to read and write real-world binary le
formats, specically BMP.
1 Background
In this assignment, you will write a program that applies three dierent lters to an image. The image will
be loaded from a BMP le, specied by the user, and then be transformed using: 1) a grayscale conversion,
2) a color shift also specied by the user, or 3) a scale operation. The resulting le will be saved back to a
BMP le that can be viewed on the system.
This document is separated into four sections: Background, Requirements, Loading Binary Files, Include
Files, and Submission. You have almost nished reading the Background section already. In Requirements,
we will discuss what is expected of you in this homework. In Loading Binary Files, we will discuss the BMP
le format and how to interact with binary les. Lastly, Submission discusses how your source code should
be submitted on Canvas.
2 Requirements [36 points]
Your program needs to implement loading a binary BMP le, applying one or more lters, and saving the
modied image. The width and height may be of any size, but you can assume that you will be capable of
storing the image data in memory. Assume that all BMP les are 24-bit uncompressed les 1 (i.e., compression
method is BI_RGB), which have a 14-bit bmp header, a 40-bit dib header, and a variable-length pixel array.
See Section 3 for further discussion of the BMP format. This is shown in Figure 1.
As a base requirement, your program must compile and run under Xubuntu (or another variant of
Ubuntu) 22.04. Several sample outputs are shown in subsection 2.1. Do not modify the header les
given except to ll in any TODOs.
Specic Requirements:
BMP Headers IO: Create structures for the headers of a BMP le (BMP header struct and DIB
header struct) and functions that read and write them. [4 Points]
* See struct BMP_Header, struct DIB_Header, readBMPHeader, writeBMPHeader, readDIBHeader,
writeDIBHeader, makeBMPHeader, and makeDIBHeader. These functions should
be implemented in a le called BMPHandler.c.
Pixel IO: Create a structure that represents a single pixel (24-bit pixel struct) and the functions
for reading and writing all pixels in BMP les. [4 Point]
* See struct Pixel, readPixelsBMP, and writePixelsBMP. These functions should be implemented
in a le called BMPHandler.c.
Input and output le names: Read the input le name and output le name from the command
line arguments. The user should be required to enter the input le name and this should be the
rst argument. The output le name is an option, it is not required and it can be in any place
in the options list. The output le option is specied by -o followed by the output le name.
Validate that the input le exists. [4 Points]
1In case you think this is making the assignment unrealistic, think again: this is the default Windows 10 BMP le format.
1
* The input le is the le to read and copy.
* The output le name is the le to write to and copy to (the new le created).
Filter command line parsing: Accept an option for grayscale (black and white). This is specied
by “-w”. Accept options for red, green, or blue color shift. These are specied by-r -g or
-b followed by an integer. Accept an option for scale. This is specied by “-s” followed by a
oat. Like the -o option described above, these can come in any order in the options list and
are optional for the user to enter. [4 Points]
Copy images: Have the ability to copy an image from a BMP le to a new BMP le. [4 Points]
Grayscale lter (image_apply_bw): convert each RGB pixel to its grayscale equivalent. [4
Points]
* In a grayscale image, each RGB component has the same value. Use the following formula to
compute it: grayscale = 0.299R + 0.587G + 0.114B 2
* This lter MUST happen before the color shift lter is applied.
Color Shift lter (image_apply_colorshift): Shift the color of the new image before saving it,
according to the options the user entered. [4 Points]
* Color shift refers to increasing or decreasing the color in a pixel by the specied amount. So,
if the user entered -b -98 all of the blue values in a pixel would be decreased by 98.
* If no color shift option was entered for a color, do not shift it.
* After the color shift, color should be clamped to 0 ~ 255. For example, color R = 100, shift
= 200. The color R after shift should be 255 (300 clamped to 255).
Scaling lter (image_apply_resize): perform nearest neighbor resize on the image. [4 Points]
* Create a new pixel array of the appropriate size (e.g., old_width * scaling_factor).
* For each pixel in the new pixel array, set its value to (roughly) the nearest neighbor in the
original. For example, if you have an image that is 100×100 and being resized to 50×50, then
the pixel at 25×25 in the smaller resized image would be the same as the one at 50×50 in the
original. (Informally, the pixelhalf way through a smaller image would be the same as the
image halfway through the larger image.
OOP: Follow the provided header le for Image to structure it as an object. The Image functions
(ve below and three lters above) should be implemented in a le called Image.c. [4 Points]
* See image_create, image_destroy, image_get_pixels, image_get_width, image_get_height.
2.1 Sample Outputs
ImageProcessor ttt.bmp -r 56
Output le name was ttt_copy.bmp.
ImageProcessor ttt.bmp -r 56 -b 78 -g 45 -o ltered1.bmp
2https://www.dynamsoft.com/blog/insights/image-processing/image-processing-101-color-space-conversion/
2
ImageProcessor ttt.bmp -w -r 56 -b 78 -g 45 -o ltered2.bmp
ImageProcessor ttt.bmp -w -o ltered3.bmp
ImageProcessor ttt.bmp -s 2.0 -o ltered4.bmp
ImageProcessor ttt.bmp -s 2.0 -w -o ltered5.bmp
3
ImageProcessor ttt.bmp -w -s .5 -r 56 -o ltered6.bmp
3 Loading Binary Files
3.1 The BMP File Format
For reference, use the BMP specication on Wikipedia: https://en.wikipedia.org/wiki/BMP_le_format.
In Figure 1, a graphical overview of a BMP le’s layout is shown. The layout is literally the meaning of
the bits/bytes in the le starting at 0 and going to the end of the le. The rst (green) region shows the
BMP header information in 14 bytes: 2 for the signature, 4 for the le size, 2 for reserved1, 2 for reserved2,
and 4 for le oset. Details on each of these (such as their data format, and contents) can be found on the
Wikipedia page. For example, the area labeled Signature should contain the characters BM to conrm that
the le is in BMP format. The second (blue region) shows the DIP header information in 40 bytes: 4 for
header size, 4 for width, 4 for height, and so on. The last region (yellow) forms a 2D array of pixels. It
stores columns from left to right, but rows are inverted to be bottom to top. Note that each row of pixels
in this section is padded to be a multiple of four bytes. For example, if a line contains two pixels (24-bits or
3-bytes each), then an additional 2-bytes of blank data will be appended.
Review the following struct called BMP_Header which holds the bmp header information. (You should
also consider creating structs to hold the dib header and pixel data.) Notice that the entries in BMP_Header
correspond to the pieces of data listed in the le format. In general, a chunk of 8-bits should be represented
as a char, a chunk of 16-bits as a short, and 32-bits as an int. (Optionally: consider using unsigned types.)
s t r u c t BMP_Header {
char s i g n a t u r e [ 2 ] ; //ID f i e l d
i n t s i z e ; // Si z e o f the BMP f i l e
sho r t r e s e rved1 ; // Appl i c a t i on s p e c i f i c
sho r t r e s e rved2 ; // Appl i c a t i on s p e c i f i c
i n t o f f s e t_pi x e l_a r r a y ; // Of f s e t where the p i x e l ar ray can be found
} ;
Plan to review Example 1 on the Wikipedia page to get a feel for the contents of each of these regions.
A recreated version of that image is provided as the attached test2.bmp le. A good exercise would be to
view the le in a hex editor like Bless.
4
Figure 1: BMP le format structure for 24-bit les without compression. Image modied from
https://en.wikipedia.org/wiki/File:BMPleFormat.png.
3.2 Loading the BMP header
This is example of code to load the BMP le header (the rst 14 bits). Figure 2 shows the output.
Figure 2: Output of base le on test2.bmp.
// sample code to read f i r s t 14 byt e s o f BMP f i l e format
FILE* f i l e = fopen (” t e s t 2 .bmp” , ” rb ” ) ;
s t r u c t BMP_Header header ;
// read bitmap f i l e header (14 byt e s )
f r e ad (&header . s i gna tur e , s i z e o f ( char ) *2 , 1 , f i l e ) ;
f r e ad (&header . s i z e , s i z e o f ( i n t ) , 1 , f i l e ) ;
f r e ad (&header . r e s e rved1 , s i z e o f ( sho r t ) , 1 , f i l e ) ;
5
f r e ad (&header . r e s e rved2 , s i z e o f ( sho r t ) , 1 , f i l e ) ;
f r e ad (&header . o f f s e t_pixe l_a r r ay , s i z e o f ( i n t ) , 1 , f i l e ) ;
p r i n t f (” s i g n a t u r e : %c%c \n” , header . s i g n a t u r e [ 0 ] , header . s i g n a t u r e [ 1 ] ) ;
p r i n t f (” s i z e : %d\n” , header . s i z e ) ;
p r i n t f (” r e s e rved1 : %d\n” , header . r e s e rved1 ) ;
p r i n t f (” r e s e rved2 : %d\n” , header . r e s e rved2 ) ;
p r i n t f (” o f f s e t_pi x e l_a r r a y : %d\n” , header . o f f s e t_pi x e l_a r r a y ) ;
f c l o s e ( f i l e ) ;
The key functions here are:
FILE * fopen ( cons t char * f i l ename , cons t char *mode )
This creates a new le stream. fopen is used with the rb mode to indicate we are reading a le in
binary mode. To read a le, use the mode wb instead of rb.
s i z e_t f r e ad ( void * ptr , s i z e_t s i z e , s i z e_t nitems , FILE * stream)
For each call to fread, we give it rst a pointer to an element of a struct containing the BMP header,
then the number of bytes to read, the number of times to read (typically 1), and the le stream to
use. Note that the order of the calls to fread denes the order in which we read data so the order
must match the le layout. (There is also a function called fwrite which works in exactly the opposite
manner. The rst parameter won’t be a pointer though.)
One function not used here but which may be useful is fseek:
i n t f s e e k ( FILE * stream , long i n t o f f s e t , i n t o r i g i n )
The purpose of fseek is to move the reading head of the FILE object by some number of bytes. It can
used to skip a number of bytes. The rst parameter to fseek is the le pointer, followed by a number
of bytes to move, and an origin. For origin, SEEK_CUR is a relative repositioning while SEEK_SET
is global repositioning.
The basic idea to support loading a BMP le will be to parse it by byte-byte (using fread) into a set of
structs. Later, you can use fwrite to write out the contents of those structs to a le stream to save the
ltered image.
3.3 Making BMP le headers
When creating a new BMP, especially one that has been resized, you will need to ll in the header of the
new le yourself. For example, you will need to ll in the compression type, the number of color planes, etc
for BMP les. The following denes default values you should use when creating an image. All values that
are not dened here, you should calculate based on the input image.
BMP Header:
Signature: BM
Reserved 1: 0
Reserved 2: 0
DIB Header:
Planes: 1
Compression: 0
Horizontal resolution: 3780
Vertical resolution: 3780
Color number: 0
Important color number: 0
6
4 Include Files
To complete this assignment, you may nd the following include les and functions useful:
stdio.h: Denes standard IO functions.
stdlib.h: Denes memory allocation functions.
string.h: Denes string manipulation functions.
char* strcat(char* dest, const char* src): The strcat() function appends the string pointed to by
src to the end of the string pointed to by dest.
char* strcpy(char* dest, const char* src): The strcpy() function copies the string pointed to, by
src to dest.
size_t strlen(const char* str): The strlen() function computes the length of the string str up to,
but not including the terminating null character.
char* strtok_r(char* str, const char* delim, char** saveptr): The strtok_r function parses a
string into a sequence of tokens.
unistd.h: Denes POSIX operating system API functions.
int getopt(int argc, char *const argv[], const char *optstring): The getopt() function is a builtin
function in C and is used to parse command line arguments.
Your solution may not include all of these include les or functions, they are only suggestions. If you want
to include any other les, please check with the instructor or TA before doing so.
5 Submission
The submission for this assignment has one part: a source code submission. The les should be zipped in
a le named “LastNameImageProcessor.zip” (e.g. “EdgarImageProcessor.zip”) attached to the homework
submission link on Canvas.
Writeup: For this assignment, no write up is required.
Source Code: Please name your main le as “LastNameImageProcessor.c” (e.g. “EdgarImageProcessor.
c”). Your ZIP should also include: BMPHandler.c, BMPHandler.h, Image.c, and Image.h.
7
C Programming, SER334
[SOLVED] SER334 Unit 3 HW2 Image Processing
$25
File Name: SER334_Unit_3_HW2_Image_Processing.zip
File Size: 320.28 KB
ser334_unit3_hw02b-1