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

余生长醉 提交于 2020-06-28 05:23:55

问题


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.


回答1:


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:

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

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

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

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

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

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)



来源:https://stackoverflow.com/questions/47667439/wrong-color-when-using-copyrect-to-copy-from-large-image-to-smaller-canvas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!