【问题标题】:How to get the most frequently appearing pixel in a bmp如何获取bmp中出现频率最高的像素
【发布时间】:2014-12-20 05:01:10
【问题描述】:

作为确定“图像的基色”的函数,我正在尝试实现以下代码:

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;
}

它应该是一种快速算法(在 RAM 方面不是很节俭)以图像的基色返回 RGB 结构。但是..它返回不正确的颜色。我做错了什么?

【问题讨论】:

  • 我停在第一行,但至少:sizeof(*counts)?当然?你在推迟一个未初始化的局部变量吗?
  • 几件事。 1) 你确定它是 24 位 bmp 吗?许多位图是 32 位的,因此它们是处理器对齐的。 2)你确定是RGB而不是更常见的BGR?
  • fread() 返回成功读取的元素数。由于您阅读了 3 个元素,因此您应该这样做 while( fread(..) == 3)
  • @Steffen。应该是while(fread(..) == 1) 不同之处在于,不是表示您正在读取三个字节,而是表示您正在读取一个 rgb,这正是您正在做的事情。这也意味着如果 rgb 的定义发生变化,您只需更改一次,此代码将自动获得正确的 rgb 大小。
  • @Lively。那么你应该使用fread(rgb, sizeof(rgb),1,fp)

标签: c colors rgb pixel bmp


【解决方案1】:

正如其他人已经指出的那样,有几个问题,最重要的一个是max_count 的索引。您可以将max_count 设为索引,然后使用构建索引时使用的反向逻辑从该索引中获取颜色。这就是 Steffen 和 user694733 的答案的工作原理。

您还可以将max_count 保留为计数,并在找到新的最大计数时分配primecolor。这样可以省去向后计算。

还有一个与填充有关的潜在问题。 BMP 格式按行存储其数据,但每行中的字节数必须是 4 的倍数。在您的情况下,图像为 262 像素宽。每行是 786 字节长,所以必须填充到 788。如果要考虑填充,则必须知道图像的宽度。

另一个令人困惑的原因是您将参数FILE_NAME 传递给您的函数,但始终打开"sample.bmp",因此您可能无法真正得到您想要的。

另外,但这是一个小问题,我认为整数立方体pow(256, 3) 更好地呈现为256 * 256 * 256

这是对我有用的变体(它需要更多的错误检查):

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;
}

【讨论】:

  • 那会慢很多。
  • @Lively:你的意思是覆盖颜色?是的,也许吧,但我不认为它“慢得多”。
  • 视情况而定。它可能只是有点慢......但随后乘以我拥有的 fps 限制。 Im 编码程序似乎非常强大。一切都有点慢..变得慢得多。
  • 好吧,实现变体和度量。我敢打赌你不会注意到很大的不同。无论如何,我想您的重点应该首先放在正确的答案上。
  • 如果你不想交换结构,你可以在返回之前交换 R 和 B 的值。 (但是,您也可以使用 Steffen 的方法并存储最常见颜色的索引,然后在根据索引计算组件时固定 RGB 顺序。)
【解决方案2】:

好的。几点: 一种是pow 函数。我发现这不是预期的。至少在我的实现上。

counts = calloc((dword)pow(256.0, 3.0), sizeof(dword));

为我工作。

fread 应该检查元素的数量:

fread (rgb, sizeof(BYTE), 3, fp) == 3

检查哪个值是最大值应该是:

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;

有了这些更改,它对我有用。唯一的事情是我得到了BGR。但这可能是由不同的图像造成的......

完整代码:

#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);
}

对于使用填充颜色 (RGB) 的 gimp 创建的图像,这会打印出 229 20 35:35,20,229。

【讨论】:

  • 我也是这么想的.. 但是一个朋友说它应该是 rgb[max_count] 假设 max_count 是索引。虽然我认为这几乎就是它的名称标识符所说的。谢谢,我试试。
  • hmm.. 现在返回相同的图像R255 G255 B255
  • 我会选择calloc(256*256*256, sizeof(dword));
  • 所有建议的内容都返回R255 G255 B255vegasoft.site90.com/pc_test.bmp
  • @Steffen,如果它适合你,让我试试上面 1:1 的代码
【解决方案3】:

除了cmets中提到的问题(不检查返回值,无效fread返回值检查,可能假设文件格式不正确......),还有max_count的问题:

if (++counts[idx] > max_count) max_count = idx;

如果您假设max_count 是一个计数器。但是,在分配中,您将 max_count 视为索引值(相当于颜色)。应该是哪个?

rgb[max_count] 在程序末尾也没有任何意义。您再次将max_count 视为索引(再次显示颜色)。 rgb 应该是最新读取的颜色,因此根本不应该使用它。

【讨论】:

    猜你喜欢
    • 2019-02-01
    • 2012-11-03
    • 1970-01-01
    • 2021-10-10
    • 1970-01-01
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 2020-08-11
    相关资源
    最近更新 更多