Project Overview
I am currently majoring in applied computer science at DHBW. In the first Semester, we have to submit the first programming project, which is an image editor in C conforming to the PGM standard. It is split in three subtasks and is to be finished till late March.
Task I | Task II | Task III |
---|---|---|
methods in _pgm.h | methods in _image.h | TUI |
Grading
x | Points |
---|---|
Task I | 15 |
Task II | 15 |
Task III | 10 |
Robustness & compilablity | 5 |
Comments & structure | 5 |
Total | 50 Points |
Project description
The PGM
editor should implement the Portable GrayMap
standard, which I will explain in the next chapter. It should
also include methods to manipulate Images in the PGM
-format. The following functions are required by the lecturer. :
README
1pgmE - portable graymap Editor
2==========
3pgmE is an extremely fast, small and efficient editor which implements the pgm standard.
4
5Requirements
6------------
7In order to build you need:
8 - make
9 - gcc
10
11Running pgmE
12------------
13Start it by running:
14
15 make run
16
17This will compile pgmE.
18
19Features
20------------
21- load pgm images
22- save pgm images
23- edit pgm images:
24 - median filter
25 - gauss operator
26 - laplace operator
27 - resize
28 - rotate
29 - threshold operato
Regarding Images
- create an image structure
- free the memory of an image structure
- deep copy an image
- load image from file system into the program
- save image to file system
Filters and Operators
median
filtergauss
filterlaplace
operatorthreshold
operatorscale
rotate
.pgm
Standard
Differentiating between the three P*
-image formats
Type | ASCII (plain) | Binary (raw) | Extension | Colors |
---|---|---|---|---|
Portable BitMap | P1 | P4 | .pbm | 0-1 (white & black) |
Portable GrayMap | P2 | P5 | .pgm | 0-255, 0-65535 (gray scale) |
Portable PixMap | P3 | P6 | .ppm | etc. |
PGM
or in my case
P2
is a image format coming from the
Netpbm Project. It consists of the two characters P
and 2
in the first line
of the file to indicate the image format. The second line contains the Dimensions of the image, e.g.: 1920 1080
. The
third line contains the max. brightness possible in the image. Line 3 till the End of the file specifies the pixel grey
values. The specification allows for comments between the first line (format indicator) and the second line (dimension
definition), prefixed with #
.
Example:
1P2
2# Shows the word "FEEP" (example from Netpbm man page on PGM)
324 7
415
50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
60 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
70 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0
80 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0
90 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0
100 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0
110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Directory structure
After reading two dozen blogs and a lot of trial and error, i settled on the following dir structure:
1.
2├── build
3│ └── main.out
4├── makefile
5├── README
6└── src
7 ├── libs
8 │ ├── image
9 │ │ ├── _image.c
10 │ │ └── _image.h
11 │ ├── pgm
12 │ │ ├── _pgm.c
13 │ │ └── _pgm.h
14 │ └── util
15 │ ├── _util.c
16 │ └── _util.h
17 └── main.c
Files:
In the following I will try to explain the file contents and what they do. The how to the what will follow in the next parts of this series.
_image.(c|h)
includes several image manipulation methods
1/**
2 * @file _image.h
3 * @author xnacly
4 * @brief includes several image manipulation methods
5 * @date 2022-02-21
6 */
7
8#ifndef _IMAGE_H_INCLUDED
9#define _IMAGE_H_INCLUDED
10#include "../pgm/_pgm.h"
11// used for: copyImage, createImage
12#include "../util/_util.h"
13// used for: compare
14
15#define PI 3.141592
16
17/**
18 * @brief Applies the median filter to the given image
19 * @param img
20 */
21Image *median(Image *img);
22
23/**
24 * @brief Applies the gauss filter to the given image
25 * @param img
26 */
27Image *gauss(Image *img);
28
29/**
30 * @brief Modifies the image using the laplace-operator
31 * @param img
32 */
33Image *laplace(Image *img);
34
35/**
36 * @brief Modifies the Image using the thresholding method
37 * @param img
38 * @param threshold
39 */
40Image *threshold(Image *img, int threshold);
41
42/**
43 * @brief Scales the given Image to the width and height specified
44 * @param img
45 * @param width
46 * @param height
47 */
48Image *scale(Image *img, int width, int height);
49
50/**
51 * @brief Rotates the Image to the given angle
52 * @param img
53 * @param angle
54 * @param brigthness
55 */
56Image *rotate(Image *img, double angle, int brigthness);
57#endif
_pgm.(c|h)
handles everything regarding images in the .pgm standard
1/**
2 * @file _pgm.h
3 * @brief handles everything regarding images in the .pgm standard
4 * http://netpbm.sourceforge.net/doc/pgm.html (Plain PGM)
5 * @author xnacly
6 * @date 2022-02-21
7 */
8#ifndef _PGM_H_INCLUDED
9#define _PGM_H_INCLUDED
10
11#define MAX_BRIGHT 255
12
13/**
14 * @brief struct to store PGM-image data in
15 */
16typedef struct {
17 int width;
18 int height;
19 int **data; // 2d pointer: Brightness values
20} Image;
21
22/**
23 * @brief creates an Image with given width and height, set every pixel to the
24 * default_brightness
25 * @param width
26 * @param height
27 * @param default_brightness
28 * @return *Image
29 */
30Image *createImage(int width, int height, int default_brightness);
31
32/**
33 * @brief frees the memory taken up by the given Image pointer
34 * @param img_pointer Image pointer created with createImage()
35 */
36void freeImage(Image *img_pointer);
37
38/**
39 * @brief copys the image from the given pointer to a new pointer
40 * @param img_pointer Image pointer created with createImage()
41 * @return *Image
42 */
43Image *copyImage(Image *img_pointer);
44
45/**
46 * @brief loads image from filesystem with given file name (without extension)
47 * @param file_name
48 * @return *Image
49 */
50Image *loadImage(char file_name[]);
51
52/**
53 * @brief saves the given pointer in a .pgm file with the given name
54 * @param file_name
55 * @param img_pointer Image pointer created with createImage()
56 * @return 0 or 1
57 */
58int saveImage(char file_name[], Image *img_pointer);
59
60#endif
_util.(c|h)
provides utility methods and defines ANSI macros for colored output, as well as an enum for the main menu selection handling
1/*
2 * _util.h provides utility methods
3 */
4#ifndef _UTIL_H_INCLUDED
5#define _UTIL_H_INCLUDED
6
7#include "../pgm/_pgm.h"
8
9#define ANSI_COLOR_RED "\x1b[91m"
10#define ANSI_COLOR_GREEN "\x1b[92m"
11#define ANSI_COLOR_YELLOW "\x1b[93m"
12#define ANSI_STYLE_BOLD "\x1b[1m"
13#define ANSI_RESET "\x1b[0m"
14
15enum {
16 SELECTION_LOAD = 0,
17 SELECTION_MEDIAN_FILTER,
18 SELECTION_GAUSS_FILTER,
19 SELECTION_LAPLACE_OPERATOR,
20 SELECTION_THRESHOLD,
21 SELECTION_SCALE,
22 SELECTION_ROTATE,
23 SELECTION_SAVE,
24 SELECTION_EXIT, // ALWAYS LATEST AVAILABLE OPTION ;)
25 SELECTION_INVALID =
26 9999 // MAKE SURE THAT THIS WILL RUN INTO THE INVALID SECTION ;)
27};
28
29/**
30 * used for qsort
31 * @param a
32 * @param b
33 * @return
34 */
35int compare(const void *a, const void *b);
36
37/**
38 * @brief converts string to integer
39 * @param text
40 * @return integer
41 */
42int toInt(const char *text);
43
44/**
45 * @brief exits the program and prints the given text highlighted red
46 * @param text
47 */
48void throw_error(char text[]);
49
50/**
51 * @brief prints the given text highlighted yellow, differs from throw_error by
52 * not exiting the program.
53 * @param text
54 */
55void throw_warning(char text[]);
56
57/**
58 * @brief checks if the selection meets certian criteria
59 * @param selection
60 * @param arr_size size of the array containing possible inputs
61 * @param edited_unsaved_image_in_memory
62 * @param image_in_memory
63 */
64int check_is_option_valid(int selection, int image_in_memory);
65#endif
main.c
takes care of printing the main menu and handling user input, runs the functions implemented in the ’libs'
Compiler setup
Compiler choice
The lecturer gave us very specific instructions on how and where to compile our project. The project has to be compiled with gcc on a Unix system. I’m using Unix for around 2 years now, this makes the requirement easily fulfillable.
Compiler flags
Some warning compiler flags are mandatory:
-fdiagnostics-color=always
(Use color in diagnostic)-Wall
(enables all warnings about constructions that are easy to avoid)-Wpedantic
(Issue all the warnings demanded by strict ISO C)
Some were added by me:
-lm
(link the math library)-g
(tell gcc to generate debugging and symbol information, necessary for debugging using gdb)
After me asking, the lecturer told us to use the C99 standard (-std=c99
).
Makefile
After learning about makefiles (before I knew about them I wrote a shell script to compile my project) and gdb I promptly used both in this project:
1cc := -fdiagnostics-color=always \
2 -Wall -Wpedantic -std=c99 \
3 src/main.c src/libs/util/_util.c \
4 src/libs/pgm/_pgm.c \
5 src/libs/image/_image.c \
6 -lm -o build/main.out
7main:
8 gcc ${cc}
9 build/main.out
10debug:
11 gcc -g ${cc}
12 gdb build/main.out
13clean:
14 rm -r build/; rm test.pgm
15pre:
16 mkdir build/
make main
compiles and runs the executable
make debug
compiles with the
-g
flag and starts gdb on the executablemake clean
removes generated images and the build folder recursive
make pre
creates the build dir, due to me being lazy
Updated, more complicated Makefile
I won’t explain too much here, due to the extensive comments and the explaining I’ve done before. If you are interested
in learning more about Makefiles
, take a look here.
New features:
- more compiler flags
- better structure
- comments
- dynamic (can compile even if i add new source files)
- made build steps dependent on the pre target
1# compiler flags
2MANDATORY_FLAGS := -fdiagnostics-color=always \
3 -Wall \
4 -Wpedantic \
5 -std=c99
6
7 # use color in diagnostics
8 # enables all construction warnings
9 # enable all warnings demanded by ISO C
10 # follow ANSI C99
11
12MY_FLAGS := -Wextra \
13 -Werror \
14 -Wshadow \
15 -Wundef \
16 -fno-common \
17
18 # set of warnings and errors not covered by -Wall
19 # all warnings cause errors
20 # warnings for shadowing variables
21 # warnings for undefined macros
22 # warnings for global variables
23
24BUILD_DIR := ./build
25SRC_DIR := ./src
26
27# finds all source files in /src/*
28FILES := $(shell find $(SRC_DIR) -name "*.c")
29
30COMPILE := $(MANDATORY_FLAGS) $(MY_FLAGS) $(FILES) -lm -o $(BUILD_DIR)/main.out
31
32# run the previously built executable
33run: main
34 $(BUILD_DIR)/main.out
35
36# compile the executable
37main: pre
38 gcc -O2 $(COMPILE)
39
40# compiles executable with debugging info and runs it with the GNU-debugger (gdb)
41debug: pre
42 gcc -g3 $(COMPILE)
43 gdb $(BUILD_DIR)/main.out
44
45# creates build dir, only if its not created yet
46pre:
47 mkdir -p $(BUILD_DIR)
48
49# removes build and test files/dirs
50.PHONY: clean
51clean:
52 rm -rf $(BUILD_DIR)
53 rm -f test.pgm
PS:
The next part will focus on the methods found in
_pgm.(c|h)
and some general issues I found myself having while working on this project.