Thread: Bitmaps I/O operations - bitmap data order

  1. #16
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by barracuda View Post
    I don't understand what you mean. What image structure?
    The image libraries you have referred to, all support reading the image row by row.

    My R8G8B8 or R10G10B10 image structure would be something like
    Code:
    typedef uint32_t  color;
    
    typedef struct {
        int width;
        int height;
        size_t stride; /* number of uint32_t's per row */
        color *pixel;
    } rgb_image;
    You would have a separate function for each image file format (JPEG, PNG, PPM, BMP, et cetera). Those functions read the image header, then create the above rgb_image structure, allocating height*stride*sizeof (color) bytes for the pixel member, then read the image file, one scan line at a time, to a temporary buffer, then convert from that temporary buffer to rgb_image structure.

    Note that if your image reader functions return an error code (but don't print any error messages), you can then have an outer "load image" function, which tries each image reader in turn, until one succeeds, or there are no more image readers available.

    In Linux systems, it is trivial to use the linker and dynamic libraries to make that plug-in-able; that is, the application can automatically support new formats if you just add that format plugin file to a directory, without recompiling anything.

    I would not mind showing how I would do this all, but I think it is more worth YOUR while if you try finding your own way, step by step.

    It would help, though, if you started a new thread for each new question, though; I'm not sure how many members stumble on follow-up questions in the middle of a thread..

  2. #17
    Registered User
    Join Date
    Sep 2014
    Posts
    235
    Yesterday and today I trimmed my conversion function rgb2hsv. It still works with 24 bit channels but I have improved the speed at least 2x. Now it can convert image rgb 4096x4096 with rgb2hsv after cca 0.27 or 0.28s. Now I am solving the second function hsv2rgb, because I want to improve it too. I will ready your yesterdays posts when I am finished.

    Right now, I need to find out why I cannot alloc memory to f1 and f2 because the program crashes (I tried to access them without pointer but it did not help too; I have one more function like this but with 2D array, using float ***f1 and that one works with *f=malloc(..) hence I wonder why this does not work).


    Code:
    void rev_255_createArray(float ** f1, float ** f2){
        *f1 = malloc((sizeof (float)) * 256 );
        *f2 = malloc((sizeof (float)) * 256 );
        int a;
        for (a=0; a<256; a++){
            (*f1)[a]=a/255; // saturation
            (*f2)[a]=a/255*360; // hue
            }
    }

  3. #18
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by barracuda View Post
    Right now, I need to find out why I cannot alloc memory to f1 and f2 because the program crashes
    I cannot see any error in your code (except that you do integer division, leading to both arrays being filled by zeros, and don't check whether the allocation fails or not).

    A minimal test program,
    Code:
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    
    int test_malloc(float **const f1, float **const f2)
    {
        int i;
    
        /* f1 and f2 must point to float pointers. */
        if (f1 == NULL || f2 == NULL)
            return errno = EINVAL;
    
        (*f1) = malloc(256 * sizeof **f1);
        (*f2) = malloc(256 * sizeof **f2);
        if (*f1 == NULL || *f2 == NULL)
            return errno = ENOMEM;
    
        for (i = 0; i < 256; i++) {
            (*f1)[i] = (float)i / 256.0f;
            (*f2)[i] = (float)i / 256.0f * 360.0f; /* or just *1.40625f */
        }
    
        return 0;
    }
    
    int main(void)
    {
        float *saturation = NULL;
        float *hue = NULL;
        int    i;
    
        if (test_malloc(&saturation, &hue)) {
            fprintf(stderr, "%s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
    
        for (i = 0; i < 256; i++)
            printf("saturation = %f, hue = %f\n", saturation[i], hue[i]);
    
        return EXIT_SUCCESS;
    }
    works just fine. Can you use the above known-working program, to locate the actual problem in your code?

  4. #19
    Registered User
    Join Date
    Sep 2014
    Posts
    235
    Ok, I found the error. I think it was wrong declaration like float *a,b; instead float *a,*b; ... Yet I realized one stupidity (redundancy) in my rgb2hsv function. I have performed 360° conversion but did not realized that this is absolutly not neccessary. The input data from user which will search for some colors or color ranges in image, will be specified in degrees but I should rather convert them to 1/255 instead 1/360 hence I don't need any conversion or division if I use 8 bit unsigned char. This would also rapidly. I just forgot that it is preferable to convert arguments to 1/255*argument. Also I realized I will need histograms, so I can calculate histograms for hue, saturation and value. I think this will just slightly reduce performance, I guess I'll have cca 0.29s for 4096x4096. I would add the histograms to the image instead color pallete 256 rgb colors would represend 3 histograms.

  5. #20
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Have you considered using 64-megabyte files to map all 256×256×256=16777216 input values to any 32-bit values?

    In Linux, it would be trivial to memory-map the mapping read-only, and use it directly to do the conversion; I think it should be the fastest option overall.

    The downside is that you'd need a 64 megabyte file per input colorspace (4 gigabyte file for a 30-bit input colorspace, and 16 gigabyte file for a full 32-bit to 32-bit mapping). You can generate those at leisure, as they're only needed during conversions. You could use a simple utility to generate the mappings, something along the lines of
    Code:
    #include <stdlib.h>
    #include <stdint.h>
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    #include <math.h>
    
    typedef enum {
        UNKNOWN = 0,
        HSV888,
        HSV101010,
    } colormodel;
    
    int main(int argc, char *argv[])
    {
        colormodel model;
        FILE *out;
    
        if (argc < 2 || argc > 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
            fprintf(stderr, "\n");
            fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
            fprintf(stderr, "       %s MODEL [ FILENAME ]\n", argv[0]);
            fprintf(stderr, "\n");
            fprintf(stderr, "This program outputs a 64-megabyte conversion table,\n");
            fprintf(stderr, "from RGB888 (red msb, blue lsb) to the specified model.\n");
            fprintf(stderr, "If FILENAME is specified, the table is saved to that file,\n");
            fprintf(stderr, "otherwise to standard output.\n");
            fprintf(stderr, "\n");
            fprintf(stderr, "Supported models:\n");
            fprintf(stderr, "       HSV888     8-bit hue (msb), saturation, value (lsb)\n");
            fprintf(stderr, "       HSV101010  10-bit hue (msb), saturation, value (msb)\n");
            fprintf(stderr, "\n");
            return EXIT_FAILURE;
        }
    
        if (!strcmp(argv[1], "HSV888") || !strcmp(argv[1], "hsv888"))
            model = HSV888;
        else
        if (!strcmp(argv[1], "HSV101010") || !strcmp(argv[1], "hsv101010"))
            model = HSV101010;
        else {
            fprintf(stderr, "%s: Unknown model.\n", argv[1]);
            return EXIT_FAILURE;
        }
    
        if (argc > 2) {
            out = fopen(argv[2], "wb");
            if (out == NULL) {
                fprintf(stderr, "%s: %s.\n", argv[2], strerror(errno));
                return EXIT_FAILURE;
            }
        } else
            out = stdout;
    
        if (model == HSV888 || model == HSV101010) {
            int red, green, blue;
    
            for (red = 0; red < 256; red++) {
                for (green = 0; green < 256; green++) {
                    const int rgmax = (green >= red) ? green : red;
    
                    for (blue = 0; blue < 256; blue++) {
                        const double alpha = 0.001953125 * (double)(2*red - green - blue);
                        const double beta = 0.00338291173353296346392 * (double)(green - blue);
                        const double hue = 0.159154943091895335768883763372514362 * atan2(beta, alpha);
                        const double chroma = sqrt(alpha * alpha + beta * beta);
                        const double value = ((blue > rgmax) ? blue : rgmax) / 255.0;
                        const double saturation = (value > 0.0) ? chroma / value : 0.0;
                        uint32_t     result = 0U;
    
                        if (model == HSV888) {
    
                            if (hue >= 1.0)
                                result = 255U << 16;
                            else
                            if (hue > 0.0)
                                result = (uint32_t)(256.0 * hue) << 16;
                            else
                                result = 0U;
    
                            if (saturation >= 1.0)
                                result |= 255U << 8;
                            else
                            if (saturation > 0.0)
                                result |= (uint32_t)(256.0 * saturation) << 8;
    
                            if (value >= 1.0)
                                result |= 255U;
                            else
                            if (value > 0.0)
                                result |= (uint32_t)(256.0 * value);
    
                        } else
                        if (model == HSV888) {
    
                            if (hue >= 1.0)
                                result = 1023U << 20;
                            else
                            if (hue > 0.0)
                                result = (uint32_t)(1024.0 * hue) << 20;
                            else
                                result = 0U;
    
                            if (saturation >= 1.0)
                                result |= 1023U << 10;
                            else
                            if (saturation > 0.0)
                                result |= (uint32_t)(1024.0 * saturation) << 10;
    
                            if (value >= 1.0)
                                result |= 1023U;
                            else
                            if (value > 0.0)
                                result |= (uint32_t)(1024.0 * value);
                        }
    
                        if (fwrite(&result, sizeof result, 1, out) != 1) {
                            fprintf(stderr, "Write error.\n");
                            return EXIT_FAILURE;
                        }
                    }
                }
    
                fprintf(stdout, "\r%5.1f%% ", 0.390625 * (double)red);
                fflush(stdout);
            }
            fprintf(stdout, "\rAll done.\n");
            fflush(stdout);
    
        } else {
            fprintf(stderr, "Oops! Model %s is not yet implemented.\n", argv[1]);
            return EXIT_FAILURE;
        }
    
        return EXIT_SUCCESS;
    }
    However, this approach would allow you to do the image processing using much more interesting colorspaces, like for example 30-bit CIELAB (10 bits per component), without any speed penalty. As far as I know, it's the best match for perceptual quantification of color, i.e. the numeric changes best match perceived changes in the color.

    Computing the conversion from sRGB (probably the most common colorspace used for image files nowadays) to CIELAB is nontrivial, but generating the 64-megabyte file should not take longer than a couple of seconds even using double-precision floating point, similar to my example above, and that's not too slow for practical work.

    (I really like the idea of being able to trade disk usage for increased speed. Of course, you should consider having a slower built-in alternative, in case the precomputed files do not exist yet.)

  6. #21
    Registered User
    Join Date
    Sep 2014
    Posts
    235
    Quote Originally Posted by Nominal Animal View Post
    You wouldn't. Instead of storing the pixel data as unsigned chars, you store them as uint32_t.

    Here is an example function that converts R, G, B as floats (0.0f to 1.0f) to a R10G10B10-format uint32_t:
    [CODE]
    result = 1023U << 20;
    What's 1023U? Where it is defined? Is it number? Why it has U on the end?M

  7. #22
    Registered User
    Join Date
    Sep 2014
    Posts
    235
    Quote Originally Posted by Nominal Animal View Post
    Have you considered using 64-megabyte files to map all 256×256×256=16777216 input values to any 32-bit values?
    That's definately disadvantage. Reading another file would slow down the calculation process. Don't you think?

    I am not convinced that R10G10B10 or B10G10R10 would be faster than using separate RGB channels (24bit). At the moment I will concentrate on developing color changing functions, things like histogram, threshold, curves (Cubic Splines) and Levels. These are basic things which my program should know to do. I forgot to mention CMYK conversion, also needed for Cubic Splines). But now I must solve the problem with crashing of my program.
    Last edited by barracuda; 02-23-2015 at 08:06 AM.

  8. #23
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by barracuda View Post
    What's 1023U? Where it is defined? Is it number? Why it has U on the end?M
    The letter at the end is used to describe the type of the numeric constant.

    1023 is an int.
    1023U is an unsigned int.
    1023L is a long.
    1023UL is an unsigned long.
    1023LL is a long long int.
    1023ULL is an unsigned long long int.
    1023.0 is a double.
    1023.0f is a float.
    1023.0L is a long double.

    Quote Originally Posted by barracuda View Post
    That's definately disadvantage. Reading another file would slow down the calculation process. Don't you think?
    Don't be blinded by your assumptions. Why would you read the color mapping file?

    If you use POSIX.1-2001 mmap() to map the file into memory, the kernel will load the accessed parts of it as needed. You don't need to "read" the entire file at all.

    I haven't written optimized colorspace conversion routines to compare the speed between the two approaches, but using a memory map is almost certainly faster if you read a large number of files at once -- say, you process a set of images.

    Quote Originally Posted by barracuda View Post
    I am not convinced that R10G10B10 or B10G10R10 would be faster than using separate RGB channels (24bit).
    Using 10 bits per component is of course not "faster"; it is just convenient, and helps you avoid quantization errors when using different colorspaces.

    Using 32-bit pixel units, with R8G8B8, is demonstrably faster on most 32-bit and 64-bit architectures (including Intel/AMD) than 24-bit units, because each pixel can be accessed as an unit, instead of three separate single-byte memory accesses.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Order of Operations help (no code)
    By tmac619619 in forum C Programming
    Replies: 4
    Last Post: 10-25-2012, 02:41 PM
  2. Order of operations question
    By GuitarNinja in forum C++ Programming
    Replies: 4
    Last Post: 04-11-2009, 06:14 AM
  3. order of operations...
    By jverkoey in forum C++ Programming
    Replies: 2
    Last Post: 05-21-2004, 11:38 PM
  4. Order of Operations
    By C-Duddley in forum C Programming
    Replies: 3
    Last Post: 12-06-2002, 08:10 PM
  5. order of operations
    By DavidP in forum A Brief History of Cprogramming.com
    Replies: 18
    Last Post: 10-31-2002, 08:51 PM