我一直致力于一个C ++ / Allegro程序,模拟给某人一个改造。有一个改变皮肤颜色的选项,我现在有了这个代码。
void doMakeup :: Foundation(MakeupMap * foundation,ALLEGRO_BITMAP * bg) {
al_lock_bitmap(bg, al_get_bitmap_format(bg), ALLEGRO_LOCK_READWRITE);
ALLEGRO_BITMAP* bmp = al_get_target_bitmap();
al_set_target_bitmap(bg);
int w = al_get_bitmap_width(bg);
int h = al_get_bitmap_height(bg);
//ALLEGRO_COLOR c = al_get_pixel(bg, state.x, state.y);
ALLEGRO_COLOR c2 = al_map_rgb(249, 230, 206);
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
ALLEGRO_COLOR c = al_get_pixel(bg, i, j);
if (SameColour(c, c2))
al_put_pixel(i, j, foundation->colour);
}
}
al_set_target_bitmap(bmp);
//move bitmap back to video memory
al_unlock_bitmap(bg);
}
代码有点做我想要的,因为它改变了颜色,但是当它接近边缘时它不会改变颜色。请帮帮我!
答案 0 :(得分:1)
正如Kenogu Labz在另一个答案中提到的,你的问题是你的色彩匹配是准确的,但是图像边缘周围的颜色是抗锯齿的或被源图像编解码器降级(这是有损图像压缩系统的常见问题,如如JPEG)。
您需要匹配Color 范围,而不是执行精确的颜色匹配。
实现此目的的最佳方法通常是设置颜色相互接近程度的容差。您尚未列出SameColor()
的代码,但您可以通过将SameColor()
更改为SimilarColor()
来获得所需的结果,其实现方式如下:
bool SimilarColor(color c1, color c2, int tolerance)
{
int rDiff = std::abs(c1.r - c2.r);
int gDiff = std::abs(c1.g - c2.g);
int bDiff = std::abs(c1.b - c2.b);
return ( rDiff < tolerance && gDiff < tolerance && bDiff < tolerance );
}
NB。这是伪代码。我现在在底部的答案中添加了一个ALLEGRO兼容的实现。
一般原则是您计算红色差异,绿色差异和蓝色差异,并确保它低于您的容差值。如果是,则返回true并执行颜色替换,如果不是,则不执行替换。
专家额外:
为了获得更好的效果,您可能需要考虑不直接替换匹配的颜色,而是更改匹配的像素的色调(保持其饱和度和值),以便您的颜色更改图像不会过度简化。这将需要颜色1和颜色2的H,S,V值,这可能直接在ALLEGRO中支持或可能需要自定义代码。
其他一些评论
请注意,您当前的代码会访问图片中的每个像素。图像中的某些非预期的任意像素可能会在您的容差范围内,并且会无意中重新着色。
为了避免这种情况,您可以将颜色匹配替换为FloodFill(即,paintbucket)类型的重新着色(虽然这更复杂并且限制您重新着色到连续区域),或者您可以使用内存中的图像来处理每个可回收部分都预先准备好具有独特的非别名像素颜色。例如,您可以在内存缓冲区中使用带有rgb(0,255,0)绿色面的源图像,但将面部重新显示为所选的色调。当用户选择更改颜色时,再次重新着色原始绿色图像。希望有道理。以下是我所描述的屏幕外源图像的示例:
附注:如果您想避免因图片文件格式而导致图片质量下降,请始终首选.png
个文件至.jpg
。
为了获得最佳效果,我完全建议使用上面描述的颜色键图像。它具有额外的优势,您可以操作原始图像并保存为.png
等无损文件格式,以便您可以根据原始代码执行精确的颜色匹配,而不需要尝试匹配颜色范围。您也永远不会遇到背景颜色太接近您的容差并且无意中更换颜色的情况。
但是,我现在还快速浏览了ALLEGRO_COLOR的ALLEGRO文档,以下方法应该使用该库执行上面显示的SimilarColor方法。在Allegro中,您似乎无法直接访问ALLEGRO_COLOR的单独组件,因此您必须先执行al_unmap_rgb
:
bool SimilarColor(ALLEGRO_COLOR c1, ALLEGRO_COLOR c2, int tolerance)
{
// unpack the colors into their r,g,b components:
unsigned char c1R, c1G, c1B, c2R, c2G, c2B;
al_unmap_rgb(c1, &c1R, &c1G, &c1B);
al_unmap_rgb(c2, &c2R, &c2G, &c2B);
// calculate the absolute red, green and blue differences:
int rDiff = std::abs((int)c1R - (int)c2R);
int gDiff = std::abs((int)c1G - (int)c2G);
int bDiff = std::abs((int)c1B - (int)c2B);
// return true only if ALL color channel diffs are below tolerance.
// NB. tolerance needs to be between 1 and 254, and a value of 35
// is probably a good start point for testing purposes
return ( rDiff < tolerance && gDiff < tolerance && bDiff < tolerance );
}
答案 1 :(得分:0)
由于anti-aliasing,基本图像边缘的像素颜色不同。