使用CopyRect从大图像复制到较小的画布时颜色错误 [英] Wrong color when using CopyRect to copy from large image to smaller canvas

查看:124
本文介绍了使用CopyRect从大图像复制到较小的画布时颜色错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I was writing a function to shrink a source image (JPG file) according to a 170 x 200px picture. The source JPG image was loaded into a TImage (Image1, fixed size of 400 x 400px, stretched to fit with aspect ration maintained), then the user will make a selection rectangle to set the area to copy, and then the image will be copied using CopyRect() onto the destination 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;
}

问题是,生成的图像颜色不正确,我观察到源越大图像,结果图像的颜色偏移更加严重。

The problem is, the resulting image color is not right, and I observed that the larger the source image, the resulting image color shifting is more worse.

这是结果的屏幕截图:

Here is the screenshot of the result:

任何主意是什么问题,如何摆脱这种变色问题?

Any Idea what's wrong and how do I get rid of this color shifting problem? Thanks in advance.

推荐答案

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

Ok I tried to recreate your problem And it looks your Image2 pixel format is the problem. I assume you got pf8bit set and jpg is pf24bit so it truncates to greenish... Also Copy rectangle is choppy and StretchDraw is way much smoother. Here comparison:

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

And the updated code of yours (Assuming Image1 is the source left image and Image2 the zoom image in the right):

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

As you can see the StretchDraw is the best not sure why they do not use the same Scaling techniques.

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;

如果您想进一步了解位图像素格式和快速直接像素格式,请参见:

If you want to know more about bitmap pixel format and fast direct pixel format see:

  • Display an array of color in C

还请注意 Assign LoadFrom。 .. 调用会更改像素格式...

Also take in mind that Assign and LoadFrom... calls change the pixel format ...

8位预览图像与您的有点不同,但是我没有您的输入图像取而代之的是,我使用您发布的屏幕截图进行裁剪,将其编码为 PNG ,然后将其重新编码为 JPG ,因此,在每个像素的基础上,颜色差异最大,甚至可能位于 +/- 1px

The 8bit preview image is a bit different from yours but I did not have your input image instead I use the screenshot you posted encoded as PNG cropped and reencode as JPG so on per pixel basis there are most likelly diferences in color and may be even in position +/-1px

您注意到区域越大,颜色失真越大。这是因为在8位像素格式上,调色板中只有256种颜色,如果图像包含更多颜色,则会截断更多颜色。所选区域越大,像素越多,因此图像中会出现不同的颜色。 GDI的色彩量化有些差,导致您看到的内容...如果想要更好的东西(如果使用8位图像作为输出),请尝试以下操作:

As you noticed the larger the area the more distortions in color. That is due to that on 8bit pixel formats you got only 256 colors in the palette and if your image contains more colors more of them got truncated. The bigger the selected area the more pixels and hence distinct colors are present in the image. GDI has a bit poor color quantization resulting in what you see... If you want something better (in case you using 8bit images as output) try these:

  • Effective gif/image color quantization?
  • Simple Dithering

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

Also as you can see the CopyRect did not fit the selection rectangle most likely due to the scaling truncation (looks like they do some poor integer math instead of subdivided DDA or bi-linear filtering to "optimize" the speed)

这篇关于使用CopyRect从大图像复制到较小的画布时颜色错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆