MorphieBlossom
MorphieBlossom

Reputation: 1117

Read out RGB data from images with C on Raspberry PI

After looking for a long time on the internet I could not find a real solution for my "problem".


What I want to do:

Compare 2 images (created with the Raspberry Pi camera in a Python script) in C. I have tried this in Python but it is too slow (+/- 1 minute per 2 images).

So I would like to try it in C. I call a C function with ctypes from my Python script. The C function expect to get 2 strings containing the paths of the 2 images. The C function must return a double variable (percentage of difference) back to the Python script.


What I tried:

I store the images as .JPG, so I searched for a c-library that can handle the jpg-format. I found a post here on stackoverflow advising CImg. I could not get that to work on my Raspberry Pi. Said it could not find the imports.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "cimg/CImg.h"

using namespace cimg_library;

double compare_pictures(const char* path1, const char* path2);

// Compares two Pictures and returns the difference value
double compare_pictures(const char* path1, const char* path2)
{
    CImg<unsigned char> image1(path1);
    CImg<unsigned char> image2(path2);
    double totalDiff = 0.0;
    unsigned int x, y;

    if (image1 == NULL || image2 == NULL)
    {
        fprintf(stderr, "One of the images does not exist\n");
        return -1;
    }

    if ((image1.width() != image2.width()) || (image1.height() != image2.height()))
    {
        fprintf(stderr, "width/height of the images must match!\n");
        return -1;
    }
    else 
    {
        for (y = 0; y < image1.height; y++)
        {
            for (x = 0; x < image1.width; x++)
            {
                totalDiff += fabs((int)image1(x, y, 0, 0) - (int)image2(x, y, 0, 0)) / 255.0;
                totalDiff += fabs((int)image1(x, y, 0, 1) - (int)image2(x, y, 0, 1)) / 255.0;
                totalDiff += fabs((int)image1(x, y, 0, 2) - (int)image2(x, y, 0, 2)) / 255.0;
            }
        }
        totalDiff = 100.0 * totalDiff / (double)(image1.width() * image1.height() * 3);
        printf("%lf\n", totalDiff);
        return totalDiff;
    }
}

CImg.h:73:18: fatal error: cstdio: No such file or directory. compilation terminated.

After some tries I gave up and went back to the internet to find another library. I found libjpeg8-dev which is suitable for the Raspberry Pi and C. Nonetheless this did not help me much either, because I couldn't really find good tutorials/documentation how to use it for my purposes.


I just want to be able to compare images created by the Raspberry Pi camera and calculate a difference percentage in a fast way (best would be in less than a second)

Upvotes: 1

Views: 1622

Answers (2)

MorphieBlossom
MorphieBlossom

Reputation: 1117

With the tips I gained I created this solution.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#ifndef STB_IMAGE_IMPLEMENTATION
    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"

    #ifndef STBI_ASSERT
        #define STBI_ASSERT(x)
    #endif
#endif

#define COLOR_R 0
#define COLOR_G 1
#define COLOR_B 2
#define OFFSET 10

double compare_pictures(const char* path1, const char* path2);

double compare_pictures(const char* path1, const char* path2)
{
    double totalDiff = 0.0, value;
    unsigned int x, y;

    int width1, height1, comps1;
    unsigned char * image1 = stbi_load(path1, &width1, &height1, &comps1, 0);

    int width2, height2, comps2;
    unsigned char * image2 = stbi_load(path2, &width2, &height2, &comps2, 0);

    if (image1 == NULL || image2 == NULL)
    {
        return -1;
    }

    if ((width1 != width2) || (height1 != height2))
    {
        return -2;
    }
    else
    {
        for (y = 0; y < height1; y++)
        {
            for (x = 0; x < width1; x++)
            {
                // Calculate difference in RED 
                value = (int)image1[(x + y*width1) * comps1 + COLOR_R] - (int)image2[(x + y*width2) * comps2 + COLOR_R];
                if (value < OFFSET && value > (OFFSET * -1)) { value = 0; }
                totalDiff += fabs(value) / 255.0;

                // Calculate difference in GREEN 
                value = (int)image1[(x + y*width1) * comps1 + COLOR_G] - (int)image2[(x + y*width2) * comps2 + COLOR_G];
                if (value < OFFSET && value > (OFFSET * -1)) { value = 0; }
                totalDiff += fabs(value) / 255.0;

                // Calculate difference in BLUE
                value = (int)image1[(x + y*width1) * comps1 + COLOR_B] - (int)image2[(x + y*width2) * comps2 + COLOR_B];
                if (value < OFFSET && value > (OFFSET * -1)) { value = 0; }
                totalDiff += fabs(value) / 255.0;
            }
        }
        totalDiff = 100.0 * totalDiff / (double)(width1 * height1 * 3);
        return totalDiff;
    }
}

Thanks to cpburnz for pointing out to use stb_image.h

stb_image.h is easy to use. I added 2 defines which should be added when using stb_image.h (as I noticed from comments in the .h file)


The difference rates where not optimal yet. I created a test environment to optimize the color checks.

I created a few 10x10 images. - base image which is entirely red (255,0,0) - test image 1 which contained red (255,0,0) and green (0, 255, 0) - test image 2 which contained red (255,0,0) and white (255, 255, 255)

Most of the time 255 was seen as 255, 254 or 253. And 0 was seen as 0, 1, 2. I created an OFFSET (10) which I use to filter the result of these minor color differences.

I created in c a small project which prints out all the red, green and blue values of both images and the difference. And also a visual representence of the difference between the images. (a '.' for same color and a '+' for different color)

Below you see the result of my test environment with the base image and the second test image.

Result of my test environment with the base image and the red/white image

Upvotes: 1

ohmu
ohmu

Reputation: 19750

Your code is C++, but your error suggests you are trying to compile it as a C program because cstdio is the C++ version of C's stdio.h.

Your compile command:

gcc -shared -o mycfile.so -fPIC mycfile.c

Is trying to compile mycfile.c as C (not C++) which is causing your error. GCC detects the type of file based upon extension (.c for C, .cpp, .cxx, .cc , and .C for C++). Rename mycfile.c to mycfile.cpp, link the C++ runtime library stdc++, and run:

gcc -lstdc++ -shared -o mycfile.so -fPIC mycfile.cpp

I am unfamiliar with CImg, but if you're open to suggestions, you can give stb_image.h a try:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "stb_image.h"

double compare_pictures(const char* path1, const char* path2);

double compare_pictures(const char* path1, const char* path2)
{
    double totalDiff = 0.0;
    unsigned int x, y;

    int width1, height1, comps1;
    unsigned char * image1 = stbi_load(path1, &width1, &height1, &comps1, 0);

    int width2, height2, comps2;
    unsigned char * image2 = stbi_load(path2, &width2, &height2, &comps2, 0);

    if (image1 == NULL || image2 == NULL)
    {
        fprintf(stderr, "One of the images does not exist\n");
        return -1;
    }

    if ((width1 != width2) || (height1 != height2))
    {
        fprintf(stderr, "width/height of the images must match!\n");
        return -1;
    }
    else 
    {
        for (y = 0; y < height1; y++)
        {
            for (x = 0; x < width1; x++)
            {
                totalDIff += fabs((int)image1[(x + y*width1) * comps1 + 0] - (int)image2[(x + y*width2) * comps2 + 0]) / 255.0;
                totalDiff += fabs((int)image1[(x + y*width1) * comps1 + 1] - (int)image2[(x + y*width2) * comps2 + 1]) / 255.0;
                totalDiff += fabs((int)image1[(x + y*width1) * comps1 + 2] - (int)image2[(x + y*width2) * comps2 + 2]) / 255.0;
            }
        }
        totalDiff = 100.0 * totalDiff / (double)(width1 * height1 * 3);
        printf("%lf\n", totalDiff);
        return totalDiff;
    }
}

Upvotes: 1

Related Questions