Reputation: 1117
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
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.
Upvotes: 1
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