NIPL is Not an Image Processing Library --------------------------------------- NIPL is a set of conventions for doing image processing in C. The conventions are the following: 1. Conventions for image data: 1.1. An image is an array of pixels 1.2. The pixels are ordered from left to right, and from top to bottom 1.3. A pixel is an array of samples 1.4. A sample is a numeric C type (typically char or float) 1.5. All the pixels of an image have the same number of samples 1.6. This number of samples is arbitrary, it can be 1, 3, or 10 or 1000 1.7. The samples are numbers, not intensities, heights, luminances 1.8. Pixels are contiguous, never broken (synonymous to 1.3) 1.9. Separate color planes, when needed, are just arrays of images 1.10. The "default" format for samples is "float" 2. Coding conventions (based on Linux Kernel Coding Style) 2.1. Tabs are 8 spaces 2.2. Lines have at most 80 columns 2.3. Functions have at most 25 lines 2.4. Functions longer than 25 lines are OK if they have some symmetry 2.7. Structs are never used in the interface of a reusable algorithm 2.8. Structs are OK for internal implementation details 2.9. In particular, there is no user-visible image struct 2.10. Algorithms may use an internal image struct adapted to their needs 2.11. Global and static variables are frowned upon 2.13. Void pointers are never explicitly casted to other pointer types 2.14. Actually, explicit casts are forbidden, use conversion functions 2.15. Struct typedefs are forbidden, use the structure tag 2.16. Pointer typedefs are forbidden 2.17. Typedefs are allowed for complicated function types 2.18. Typedefs are allowed for plain C scalar data types 2.19. A typedef always ends in "_t" (e.g., "sample_t", "getpixel_t") 2.20. "For" loops always go from 0 to N, "while" loops are avoided 2.21. All functions are declared static except for public API functions 2.22. Order of function arguments: (output, input, options) 2.23. Exception to 2.21: (state, output, input, options) 2.24. The ideal internal function: * is static * has a very long name * has two or three single-letter arguments * has none or very few local variables * is only called once 2.25. The ideal interface function: * is the only non-static function in its file * has a very long name * has a few arguments with long names explaining their purpose * has many single-letter local variables * does not do any actual computation, just passes stuff around 2.26. Every "file.c" contains an example "main", usable standalone 3. Naming conventions 3.1. All C symbols are 100% lowercase 3.2. All macros are 100% uppercase 3.3. Function names are very long and descriptive 3.4. Local variables are preferably single letters 3.5. Struct tags have long and descriptive names 3.6. Struct fields have short names, often single letters 3.7. Global and static variables must have very long and obnoxious names 3.8. The name of global and static variables starts by "global_" 3.9. Typical names for image arguments: x, y, z, in, out 3.10. Names for image sizes: w (width), h (height), pd (pixel dimension) 3.11. Image sizes as arguments go together with their images 3.12. Example of 3.11: void zoom_out(float *y, int wy, int hy, float *x, int wx, int hx) 3.13. How to traverse all the pixels of an image (note the indentation): for (int j = 0; j < h; j++) for (int i = 0; i < w; i++) x[j*w + i] = 0; 4. File organization 4.1. Any file "x.c" can be compiled to give a useful program "x" 4.2. To avoid creation of the CLI, compile with -DHIDE_ALL_MAINS 4.3. To reuse the functions from another file #include "x.c" 4.4. Header files "x.h" are strongly discouraged for most algorithms MOTIVATION ---------- I often try to convince people that, when programming in C, an "image struct" is useless because it creates more problems than it solves. I contend that it is better to use raw C data. Thus, an image is represented by three variables: a pointer to the RGB pixel values, an integer representing the width and an integer representing the height. The typical reply that I receive is that this data is incomplete to describe an image: Are the pixels stored by rows or by columns? Are the RGB values contiguous or separated into planes? These problems are often solved by creating an image "object", that hides them as implementation details and provides a well-defined interface. I agree that this is a good solution, but not the only one. A different solution, as proposed by NIPL, is to fix definite answers to these questions. Thus, users of NIPL are forced to arrange their data in a certain order, just as users of other libraries are forced to call the interface functions with a certain order of arguments. The amount of information needed to describe an image interface is similar to the amount of information needed to describe the NIPL conventions, so there's no net gain here. However, the amount of NIPL code is zero, while the amount of code for other libraries is non-zero, giving an infinite advantage. Besides these philosophical reasons, the most poignant practical justification for NIPL is that it produces code which is trivially modular and reusable. I do not care if NIPL code is difficult to write, but I want that algorithms implemented in NIPL be very easy to copy-paste into other, non-NIPL libraries. # vim:set tw=69 filetype=text spell spelllang=en: