使用CopyRect从大图像复制到较小的画布时颜色错误

时间:2017-12-06 05:31:58

标签: canvas graphics c++builder timage

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

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

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

以下是结果的屏幕截图: Color shifted

任何想法有什么不对,我如何摆脱这种色移问题?提前谢谢。

1 个答案:

答案 0 :(得分:1)

好的,我尝试重新创建您的问题看起来您的Image2像素格式是问题所在。我假设你设置了pf8bit jpg pf24bit所以它会截断为绿色...而且复制矩形是不稳定的,StretchDraw更平滑。在这里比较:

您的更新代码(假设Image1是源左图像,Image2右侧是缩放图像:

preview

正如您所看到的,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;

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

另请注意,AssignLoadFrom...调用会更改像素格式...

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

的位置

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

同样你可以看到CopyRect最不适合选择矩形,这很可能是由于缩放截断(看起来他们做了一些不好的整数数学而不是细分 DDA 双线性过滤以“优化”速度)