使用 CopyRect 从大图像复制到小图像时颜色错误 canvas

Wrong color when using CopyRect to copy from large image to smaller canvas

我正在编写一个函数,根据 170 x 200 像素的图片缩小源图像(JPG 文件)。将源 JPG 图像加载到 TImage 中(Image1,固定大小为 400 x 400px,拉伸以适应保持纵横比),然后用户将制作一个选择矩形来设置要复制的区域,然后图像将被复制在目标 TImage (Image2) 上使用 CopyRect()。

void __fastcall TSizePhotoForm::Button3Click(TObject *Sender)
{
    float scale, base  = 400.0f;
    TRect crect; // copy rect

    Image2->Width  = 170;
    Image2->Height = 200;

    Image2->Canvas->CopyMode = cmSrcCopy;
    TJPEGImage *img = new TJPEGImage();
    img->LoadFromFile(fname);
    Graphics::TBitmap *bmp = new Graphics::TBitmap;
    bmp->Assign(img);
    scale = (float)img->Width / base;
    crect.Left   = srect.Left * scale; // srect = source rect
    crect.Top    = srect.Top  * scale;
    crect.Right  = crect.Left + (srect.Width()  * scale);
    crect.Bottom = crect.Top  + (srect.Height() * scale);
    Image2->Canvas->CopyRect(TRect(0, 0, w, h), bmp->Canvas, crect);
    delete img;
    delete bmp;
}

问题是,生成的图像颜色不对,我观察到源图像越大,生成的图像颜色偏移越差。

结果截图如下:

知道哪里出了问题吗?我该如何解决这个颜色偏移问题?提前致谢。

好的,我试着重现你的问题看起来你的 Image2 像素格式是问题所在。我假设你设置了 pf8bit 并且 jpgpf24bit 所以它被截断为绿色......另外复制矩形是不稳定的并且 StretchDraw 太多了顺畅。这里比较:

以及您更新后的代码(假设 Image1 是左边的源图像,Image2 是右边的缩放图像):

如您所见,StretchDraw 是最好的,不知道为什么他们不使用相同的缩放技术。

int h,w;
float scale, base  = 400.0f;
TRect crect; // copy rect
TRect srect; // I assume some mouse selected rectangle I set it manualy instead
// init and load
Image2->Canvas->CopyMode = cmSrcCopy;
TJPEGImage *img = new TJPEGImage();
img->LoadFromFile("in.jpg");
Graphics::TBitmap *bmp = new Graphics::TBitmap;
bmp->Assign(img);
// I assume this is how you are rendering/store your left (source) image
Image1->Width  = bmp->Width;            // set dersired size
Image1->Height = bmp->Height;
Image1->Left   = 10;
Image1->Top    = 10;
Image1->Canvas->Draw(0,0,bmp);
// I assume this is your mouse selection
srect=TRect(90,34,192,154);
h=120; w=102;
// just render into Image1 for visual check
Image1->Canvas->Pen->Color=clYellow;
Image1->Canvas->Pen->Style=psDashDot;
Image1->Canvas->Brush->Style=bsClear;
Image1->Canvas->Rectangle(srect);
Image1->Canvas->Pen->Style=psSolid;
Image1->Canvas->Brush->Style=bsSolid;
// place and resize Image2 next to Image1
Image2->Top=10;
Image2->Left=Image1->Width+20;
Image2->Width  = 170;
Image2->Height = 200;
// scaling
scale = (float)img->Width / base;
crect.Left   = srect.Left * scale; // srect = source rect
crect.Top    = srect.Top  * scale;
crect.Right  = crect.Left + (srect.Width()  * scale);
crect.Bottom = crect.Top  + (srect.Height() * scale);
// this is how you change the pixelformat
Image2->Picture->Bitmap->PixelFormat=pf32bit;

// your copy rect alternative
// Image2->Canvas->CopyRect(TRect(0,0,h,w), bmp->Canvas, crect);

// my stretch draw alternative
Graphics::TBitmap *tmp=new Graphics::TBitmap;
tmp->PixelFormat=pf32bit;
tmp->SetSize(srect.Width(),srect.Height());
tmp->Canvas->CopyRect(TRect(0,0,srect.Width(),srect.Height()), bmp->Canvas, srect);
Image2->Canvas->StretchDraw(TRect(0,0,srect.Width(),srect.Height()),tmp);
delete tmp;

// exit
delete img;
delete bmp;

如果您想了解更多关于位图像素格式和快速直接像素格式的信息,请参阅:

  • Display an array of color in C

还要记住 AssignLoadFrom... 调用会更改像素格式 ...

8 位预览图像与您的有点不同,但我没有您的输入图像,而是使用您发布的编码为 PNG 的屏幕截图,裁剪并重新编码为 JPG 所以在每个像素的基础上,颜色很可能存在差异,甚至可能在位置 +/-1px

如您所见,区域越大,颜色失真越多。这是因为在 8 位像素格式上,您在调色板中只有 256 种颜色,如果您的图像包含更多颜色,则更多颜色会被截断。所选区域越大,图像中的像素越多,因此颜色越明显。 GDI 的颜色量化有点差导致你看到的...如果你想要更好的东西(如果你使用 8 位图像作为输出)试试这些:

  • Effective gif/image color quantization?

此外,如您所见,CopyRect 不适合选择矩形,很可能是由于缩放截断(看起来他们做了一些糟糕的整数数学而不是细分 DDA双线性过滤到"optimize"速度)