As a function to determine "the base color of an image" I'm trying to implement the following code:
typedef unsigned long dword;
typedef unsigned short word;
typedef unsigned char BYTE;
typedef struct
{
BYTE R;
BYTE G;
BYTE B;
} RGB;
RGB
bitfox_get_primecolor_direct
(char *FILE_NAME)
{
RGB primecolor;
BYTE rgb[3];
dword *counts;
dword max_count = 0;
FILE* fp = fopen("sample.bmp", "rb");
counts = calloc(pow(256, 3), sizeof(*counts));
fseek(fp, 54, SEEK_SET);
while (fread (rgb, sizeof(BYTE), 3, fp) == 1)
{
dword idx = (((dword)rgb[0]) << 16) | (((dword)rgb[1]) << 8) | (dword)rgb[2];
if (++counts[idx] > max_count) max_count = idx;
}
primecolor.R = (rgb[max_count] >> 16) & 0xFF;
primecolor.G = (rgb[max_count] >> 8) & 0xFF;
primecolor.B = rgb[max_count] & 0xFF;
free(counts);
fclose(fp);
return primecolor;
}
It is supposed to be a fast algorithm ( not really thrifty when it comes to RAM ) to return RGB struct, with the base color of an image. However.. it returns the incorrect color. What am i doing wrong?
As others have pointed out already, there are several issues, the most important one the indexing of max_count
. You can make max_count
an index and then derive the colour from that index with the reverse logic that you used when constructing the index. This is how Steffen's and user694733's answers work.
You could also keep max_count
as a count and assign primecolor
when a new max count is found. That saves you the backward calculation.
Thee's a potential other issue to do with padding. The BMP format stores its data line-wise, but the number of bytes in each line must be a multiple of 4. In your case, the image is 262 pixels wide. Each line is 786 bytes long, so it must be padded to 788. If you want to take padding into account, you must know the width of the image.
Another source of confusion is that you pass a parameter FILE_NAME
to your function, but always open "sample.bmp"
, so you might not really get what you want.
Also, but this is a niggle, I think that the integer cube pow(256, 3)
is better rendered as 256 * 256 * 256
.
Here's varaint that works for me (it needs more error checking):
RGB bitfox_get_primecolor_direct(char *FILE_NAME)
{
RGB primecolor = {0, 0, 0};
BYTE hdr[54];
dword *counts;
dword max_count = 0;
word w, h;
word i, j;
FILE* fp = fopen(FILE_NAME, "rb");
counts = calloc(256 * 256 * 256, sizeof(*counts));
// Read header to get width and height
fread(hdr, sizeof(hdr), 1, fp);
w = (hdr[19] << 8) | hdr[18];
h = (hdr[23] << 8) | hdr[22];
// Loop over pixels
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
RGB rgb;
dword idx;
if (fread(&rgb, 3, 1, fp) < 1) {
fprintf(stderr, "Unexpected end of file.\n");
exit(1);
}
idx = (rgb.R << 16) | (rgb.G << 8) | rgb.B;
if (++counts[idx] > max_count) {
max_count = counts[idx];
primecolor = rgb;
}
}
// Treat padding
j = 3 * w;
while (j++ % 4) getc(fp);
}
free(counts);
fclose(fp);
return primecolor;
}
Alright. Several points: One is the pow
function. I found this not to behave a expected. At least on my implementation.
counts = calloc((dword)pow(256.0, 3.0), sizeof(dword));
works for me.
fread should check for the number of elements:
fread (rgb, sizeof(BYTE), 3, fp) == 3
The checking which value is max should probably be:
if (++(counts[idx]) > counts[max_count]) {
max_count = idx;
}
And finally the extraction of the color can be done by:
primecolor.R = max_count >> 16 & 0xFF;
primecolor.G = max_count >> 8 & 0xFF;
primecolor.B = max_count & 0xFF;
With this changes it works for me. The only thing is that I get BGR. But this may result from a different image...
Complete code:
#include <stdio.h>
#include <math.h>
typedef unsigned long dword;
typedef unsigned short word;
typedef unsigned char BYTE;
typedef struct
{
BYTE R;
BYTE G;
BYTE B;
} RGB;
void main (void)
{
RGB primecolor;
BYTE rgb[3];
dword *counts;
dword max_count = 0;
FILE* fp = fopen("c:/tmp/test.bmp", "rb");
counts = calloc((dword)pow(256.0, 3.0), sizeof(dword));
fseek(fp, 54, SEEK_SET);
while (fread (rgb, sizeof(BYTE), 3, fp) == 3)
{
BYTE r = rgb[0];
BYTE g = rgb[1];
BYTE b = rgb[2];
dword idx = (((dword)rgb[0]) << 16) | (((dword)rgb[1]) << 8) | (dword)rgb[2];
if (++(counts[idx]) > counts[max_count]) {
max_count = idx;
}
}
primecolor.R = max_count >> 16 & 0xFF;
primecolor.G = max_count >> 8 & 0xFF;
primecolor.B = max_count & 0xFF;
free(counts);
fclose(fp);
printf("%d %d %d ",primecolor.R,primecolor.G,primecolor.B);
}
This prints out 229 20 35 for an image create with gimp filled with color (RGB): 35,20,229.
In addition to issues mentioned in comments (not checking return values, invalid fread
return value check, possibly assuming incorrect file format...), there is an issue with max_count
:
if (++counts[idx] > max_count) max_count = idx;
In condition you assume that max_count
is a counter. However, in assigment you treat max_count
as index value (which is equivalent of color). Which one it's supposed to be?
rgb[max_count]
at the end of program makes no sense either. Again you are treating max_count
as index (again color). rgb
is supposed to be latest read color, thus it shouldn't be used used at all.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.