Неправильный цвет при использовании CopyRect для копирования с большого изображения на меньший холст

Я писал функцию сжатия исходного изображения (файл JPG) в соответствии с изображением размером 170 x 200 пикселей. Исходное 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;
}

Проблема в том, что результирующий цвет изображения не правильный, и я заметил, что чем больше исходное изображение, тем резче смещение цвета результирующего изображения.

Вот скриншот результата: Цвет сдвинут

Любая идея, что не так и как мне избавиться от этой проблемы смещения цвета? Заранее спасибо.

1 ответ

Решение

Хорошо, я пытался воссоздать твою проблему Image2 формат пикселей это проблема. Я полагаю, вы получили pf8bit установить и JPG pf24bit так что он усекается до зеленоватого... Также копировать прямоугольник является изменчивым и 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;

Если вы хотите узнать больше о растровом пиксельном формате и быстром прямом пиксельном формате, смотрите:

Также имейте в виду, что Assign а также LoadFrom... звонки меняют формат пикселя...

Предварительное 8-битное изображение немного отличается от вашего, но у меня не было вашего входного изображения, вместо этого я использую скриншот, который вы разместили, в кодировке PNG, обрезанный и перекодированный в JPG, так что в расчете на пиксель существуют наиболее вероятные различия в цвете, и они могут быть даже в позиция +/-1px

Как вы заметили, чем больше площадь, тем больше искажений в цвете. Это связано с тем, что в 8-битных пиксельных форматах вы получаете только 256 цветов в палитре, и если ваше изображение содержит больше цветов, больше из них усекается. Чем больше выделенная область, тем больше пикселей и, следовательно, различных цветов присутствуют на изображении. GDI имеет немного плохое квантование цветов, что приводит к тому, что вы видите... Если вы хотите что-то лучше (если вы используете 8-битные изображения в качестве вывода), попробуйте следующее:

Также, как вы можете видеть CopyRect не уместился в прямоугольник выбора, скорее всего, из-за усечения масштабирования (похоже, что они используют плохую целочисленную математику вместо подразделенного DDA или билинейной фильтрации, чтобы "оптимизировать" скорость)

Другие вопросы по тегам