问题
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