2

我有一个 RGB 图像,这样:

Img[3*(row*imgWidth+column)+0], //R
Img[3*(row*imgWidth+column)+1], //G
Img[3*(row*imgWidth+column)+2]  //B

表示每个 RGB 的每个像素的强度值。什么是在图像周围填充 0(0-255 比例)边框的干净方法?边框可以调整为任意宽度。

我唯一能想到的就是手动插入行和列,这变成了一段非常冗长的代码。

对不起,但图书馆不是我在这里寻找的

4

6 回答 6

3

您还没有提到该平台,但如果您使用的是 x86,英特尔的集成性能基元 (IPP)库,特别是其图像处理库,将在所选颜色的图像周围添加边框,或者在现有图像上就地,或作为复制、调整大小或其他几个操作的一部分。这是非常高效的代码。

于 2012-09-05T00:49:03.577 回答
2

这是我能想到的最易读的方式,这也是相当有效的。假设您有一个尺寸Width为、、和Height的所需边距的图像。分配一个用零填充的缓冲区。在 C++ 中,您可以使用一种方便的构造函数:LeftRightTopBottomWidth + Left + RightHeight + Top + Bottomstd::vector

const auto Channels = 3;

const auto TargetWidth = Width + Left + Right;
const auto TargetHeight = Height + Top + Bottom;

std::vector<uint8_t> target(TargetWidth * TargetHeight * Channels);

C 函数calloc()也是一个选项。接下来,将源图像中的每一行复制到目标图像,从垂直偏移量Top和水平偏移量开始Left。用于std::copy()复制行,并以行优先顺序运行外部循环以避免缓存未命中:

for (int y = 0; y < Height; ++y) {
    const auto* const source_row = &source[y * Width * Channels];
    auto* const target_row = &target[(y + Top) * TargetWidth * Channels + Left];
    std::copy(source_row, source_row + Width * Channels, target_row);
}

如果您可以使用 32 位 RGB0 或 RGBA 而不是 24 位 RGB,您可能会看到更快的复制,这要归功于更一致的对齐方式,std::copy()或者memcpy()已经得到了很好的优化。如果您可以使用OpenMP,您还可以尝试并行化循环:

#pragma omp parallel for
for (int y = 0; y < Height; ++y) {
    // ...
}
于 2012-09-05T01:26:53.470 回答
2

如果您已经在使用图像处理库(Intel Performance Primitives、ImageMagick、Windows 等),请查找已经执行此操作的现有函数。如果作者不厌其烦地使用 MMX 或 SSE 指令,库的实现可能会更快。

如果做不到这一点,您可以使用类似于 Ed S. 的解决方案自行开发。这是一个草图。可以进行额外的微优化,例如将 memset 调用的数量减半。

unsigned char* padWithBlack(const unsigned char* in,
                            int inWidth, int inHeight,
                            int thickness)
{
    unsigned char* out = malloc((inWidth + thickness * 2) *
                                (inHeight + thickness * 2) * 3);

    // top border
    int topOrBottomOutBytes = (inWidth + thickness * 2) * thickness * 3;
    memset(out, 0, topOrBottomOutBytes);

    // middle section
    unsigned char* inRow  = in;
    unsigned char* outRow = out + topOrBottomOutBytes;
    for (int inY = 0; inY < inHeight; inY++) {
        // left border
        memset(outRow, 0, thickness * 3);
        outRow += thickness * 3;
        // center section
        memcpy(outRow, inRow, inWidth * 3);
        outRow += inWidth * 3;
        inRow  += inWidth * 3;
        // right border
        memset(outRow, 0, thickness * 3);
        outRow += thickness * 3;
    }

    // bottom border
    memset(out, 0, topOrBottomOutBytes);
}
于 2012-09-05T02:01:32.983 回答
0

你看过任何预先构建的图像处理库吗?我过去使用过ImageMagick - 这不是最容易安装的东西,但一旦你让它工作,它可以做很多事情。包括你想要的。

如果你真的需要让你的程序进行图像处理,它是开源的。虽然我们说的是“超过 400,000 行 C 代码”,但如果可以避免的话,您可能希望避免接触它。

希望有帮助!

于 2012-09-05T00:37:20.797 回答
0

一种考虑是生成的图像必须大于源图像,除非边框覆盖了原始图像。后一种情况只涉及覆盖有问题的像素,但是“更大的输出”情况将需要首先为新图像分配空间。一旦分配了新的(可能通过一些生成适当标题的库),您需要逐行 memcopy 旧的(以解决差距),使用复制矩形样本的快速 api,如 bitblt ,或使用本质上相同的库。该方法将取决于图像格式本身;简单的bits[width*height-1] = {0};位图等格式将与逐行 memcpy 或 bit blt 一起使用,但像 pngs 这样的压缩格式可能需要一个可以解释数据结构的库。如果您的环境已经有这些库(例如 MSVS 或 Qt),您也可以使用它。但如果这不是一个选项,您可以只使用其他海报推荐的第三方库。

于 2012-09-05T01:47:32.787 回答
-1

所以...我不会建议将库用于如此简单的事情。如果你以后需要进行更高级的操作肯定会考虑它,但是画一个边框?没门。

这是我周围的一些代码。它在给定位置绘制一个黑色矩形。如果您愿意,您可以轻松地将其更改为仅在边缘着色。请注意,它假定图像为 24 bpp。

void WriteDebugImage( const unsigned char* inImageBuf, int inImageWidth, int inImageHeight, int left, int top, int right, int bottom, unsigned char* outImage )
{   
    // copy the old image into the new one
    memcpy( outImage, inImageBuf, inImageWidth * inImageHeight * 3 );
    for( int x = left; x <= right; ++x )
    {
        outImage[3 * top * inImageWidth + 3 * x]     = 0;    //Top Edge
        outImage[3 * top * inImageWidth + 3 * x + 1] = 0;    //Top Edge
        outImage[3 * top * inImageWidth + 3 * x + 2] = 0;    //Top Edge

        outImage[3 * bottom * inImageWidth + 3 * x]     = 0; //Bottom Edge
        outImage[3 * bottom * inImageWidth + 3 * x + 1] = 0; //Bottom Edge
        outImage[3 * bottom * inImageWidth + 3 * x + 2] = 0; //Bottom Edge
    }

    for (int y = top; y <= bottom; ++y)
    {
        outImage[3 * y * inImageWidth + 3 * left]     = 0;  //Left Edge
        outImage[3 * y * inImageWidth + 3 * left + 1] = 0;  //Left Edge
        outImage[3 * y * inImageWidth + 3 * left + 2] = 0;  //Left Edge

        outImage[3 * y * inImageWidth + 3 * right]     = 0;  //Right Edge
        outImage[3 * y * inImageWidth + 3 * right + 1] = 0;  //Right Edge
        outImage[3 * y * inImageWidth + 3 * right + 2] = 0;  //Right Edge
    }
}
于 2012-09-05T00:39:01.920 回答