简介
使用库:easyx
语言:C++
描述:只需一次,就能把人物外围所有在误差范围内的颜色换成HIDE_COLOR目标色的算法,
不会有外部间隙没清理到。
- 目录
1.第一种方式:逐点扫描
2.第二种方式:直线扫描
3.第三种方式:蔓延扫描
4.第四种方式:四方格式扫描
使用该算法前应该具有的前置代码:
#define HIDE_COLOR 0xFF555555//设置目标颜色
struct color_group{//先建立一个用来存储颜色的结构体
BYTE r;//这三个是要换成HIDE_COLOR的颜色的RGB
BYTE g;
BYTE b;
BYTE rl;//这六项是要被替换的颜色所允许的误差范围,即用来判断是否是相似色
BYTE gl;
BYTE bl;
BYTE rr;
BYTE gr;
BYTE br;
BYTE rs;//这三项是扫描到的点本身的RGB三色
BYTE gs;
BYTE bs;
};
在具备了上述前置代码后,则开始使用以下代码开始计算:
/计算准备
IMAGE img;
int w, h;
DWORD *src;
COLORREF color = 0xFB8BFF;//要被替换的颜色 这里是粉色
int color_go = 20;//偏差20范围的颜色视作为相似
color_group p;//生成实例
p.r = GetRValue(color);//获得要被替换的颜色的RGB三色
p.g = GetGValue(color);
p.b = GetBValue(color);
p.rl = p.r < color_go ? 0 : p.r - color_go ;//COLOR_GO是误差值,这里用来获取误差区间
p.gl = p.g < color_go ? 0 : p.g - color_go ;
p.bl = p.b < color_go ? 0 : p.b - color_go ;
p.rr = p.r > 255 - color_go ? 255 : 255 - color_go ;
p.gr = p.g > 255 - color_go ? 255 : 255 - color_go ;
p.br = p.b > 255 - color_go ? 255 : 255 - color_go ;
loadimage(&img, TEXT("a.jpg"));//装载目标图像:TEXT("路径+文件名")这种写法是为了自适应当前编码
w = img.getwidth(), h = img.getheight();//获取图像宽高
src = GetImageBuffer(&img);//得到图像缓冲区
/算法实现
//函数实现:
void quit_color(int i, int j, int &w, int &h, COLORREF& color, DWORD *(&src), color_group &p) {
//i和j是当前点的坐标x与y的意思。w与h是要扫描的图片的宽和高。color是要替换的颜色,p是传进来的颜色结构体
if (j - 1 >= 0) {//如果可以向上走
p.rs = GetRValue(src[i + (j - 1) * w]);//获得该点RGB三色准备比较
p.gs = GetGValue(src[i + (j - 1) * w]);
p.bs = GetBValue(src[i + (j - 1) * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i + (j - 1) * w] != HIDE_COLOR) {
src[i + (j - 1) * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i, j - 1, w, h, color, src, p);//向上走一个像素
}
}
if (i + 1 < w) {//如果可以向右走
p.rs = GetRValue(src[i + 1 + j * w]);//获得该点RGB三色准备比较
p.gs = GetGValue(src[i + 1 + j * w]);
p.bs = GetBValue(src[i + 1 + j * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i + 1 + j * w] != HIDE_COLOR) {
src[i + 1 + j * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i + 1, j, w, h, color, src, p);//向右走一个像素
}
}
if (j + 1 < h) {//如果可以向下走
p.rs = GetRValue(src[i + (j + 1) * w]);//获得该点RGB三色准备比较
p.gs = GetGValue(src[i + (j + 1) * w]);
p.bs = GetBValue(src[i + (j + 1) * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i + (j + 1) * w] != HIDE_COLOR) {
src[i + (j + 1) * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i, j + 1, w, h, color, src, p);//向下走一个像素
}
}
if (i - 1 > 0) {//如果可以向左走
p.rs = GetRValue(src[i - 1 + j * w]);
p.gs = GetGValue(src[i - 1 + j * w]);
p.bs = GetBValue(src[i - 1 + j * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i - 1 + j * w] != HIDE_COLOR) {
src[i - 1 + j * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i - 1, j, w, h, color, src, p);//向左走一个像素
}
}
return;
}
完整实例:
#include <graphics.h>
#include <conio.h>
#define HIDE_COLOR 0xFF555555//设置目标颜色
struct color_group {//先建立一个用来存储颜色的类
BYTE r;//这三个是要换成HIDE_COLOR的颜色的RGB
BYTE g;
BYTE b;
BYTE rl;//这六项是要被替换的颜色所允许的误差范围
BYTE gl;
BYTE bl;
BYTE rr;
BYTE gr;
BYTE br;
BYTE rs;//这三项是扫描到的点本身的RGB三色
BYTE gs;
BYTE bs;
};
//函数实现:
void quit_color(int i, int j, int &w, int &h, COLORREF& color, DWORD *(&src), color_group &p) {
//i和j是当前点的坐标x与y的意思。w与h是要扫描的图片的宽和高。color是要替换的颜色,p是传进来的颜色结构体
if (j - 1 >= 0) {//如果可以向上走
p.rs = GetRValue(src[i + (j - 1) * w]);//获得该点RGB三色准备比较
p.gs = GetGValue(src[i + (j - 1) * w]);
p.bs = GetBValue(src[i + (j - 1) * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i + (j - 1) * w] != HIDE_COLOR) {
src[i + (j - 1) * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i, j - 1, w, h, color, src, p);//向上走一个像素
}
}
if (i + 1 < w) {//如果可以向右走
p.rs = GetRValue(src[i + 1 + j * w]);//获得该点RGB三色准备比较
p.gs = GetGValue(src[i + 1 + j * w]);
p.bs = GetBValue(src[i + 1 + j * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i + 1 + j * w] != HIDE_COLOR) {
src[i + 1 + j * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i + 1, j, w, h, color, src, p);//向右走一个像素
}
}
if (j + 1 < h) {//如果可以向下走
p.rs = GetRValue(src[i + (j + 1) * w]);//获得该点RGB三色准备比较
p.gs = GetGValue(src[i + (j + 1) * w]);
p.bs = GetBValue(src[i + (j + 1) * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i + (j + 1) * w] != HIDE_COLOR) {
src[i + (j + 1) * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i, j + 1, w, h, color, src, p);//向下走一个像素
}
}
if (i - 1 > 0) {//如果可以向左走
p.rs = GetRValue(src[i - 1 + j * w]);
p.gs = GetGValue(src[i - 1 + j * w]);
p.bs = GetBValue(src[i - 1 + j * w]);
if (p.rs >= p.rl && p.rs <= p.rr && p.gs >= p.gl && p.gs <= p.gr && p.bs >= p.bl && p.bs <= p.br&&src[i - 1 + j * w] != HIDE_COLOR) {
src[i - 1 + j * w] = HIDE_COLOR;//如果在这个范围之内,则被替换
quit_color(i - 1, j, w, h, color, src, p);//向左走一个像素
}
}
return;
}
int main()
{
IMAGE img;
int w, h;
DWORD *src;
COLORREF color = 0xFB8BFF;//要被替换的颜色 这里是粉色
int color_go = 20;//偏差20范围的颜色视作为相似
color_group p;//生成实例
p.r = GetRValue(color);//获得要被替换的颜色的RGB三色
p.g = GetGValue(color);
p.b = GetBValue(color);
p.rl = p.r < color_go ? 0 : p.r - color_go;//COLOR_GO是误差值,这里用来获取误差区间
p.gl = p.g < color_go ? 0 : p.g - color_go;
p.bl = p.b < color_go ? 0 : p.b - color_go;
p.rr = p.r > 255 - color_go ? 255 : 255 - color_go;
p.gr = p.g > 255 - color_go ? 255 : 255 - color_go;
p.br = p.b > 255 - color_go ? 255 : 255 - color_go;
loadimage(&img, TEXT("a.png"));//装载目标图像:TEXT("路径+文件名")这种写法是为了自适应当前编码
w = img.getwidth(), h = img.getheight();//获取图像宽高
src = GetImageBuffer(&img);//得到图像缓冲区
initgraph(w*2, h);//准备演示
HWND hWnd = GetHWnd();//获得窗口句柄
int ExdStyle = (int)GetWindowLong(hWnd, GWL_EXSTYLE);
SetWindowLong(hWnd, GWL_EXSTYLE, ExdStyle | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, HIDE_COLOR, 255, LWA_COLORKEY);//指定为颜色HIDE_COLOR透明
setbkcolor(HIDE_COLOR);//设置设备背景色
cleardevice();//清洗背景
putimage(0, 0, &img);//输出原图像
quit_color(0, 0, w, h, color, src, p);//开始除去除该颜色
putimage(img.getwidth(), 0, &img);//输出对比图像
_getch();//如果这里出错就换成getch();
closegraph();
return 0;
}
这次就直接上一个我特意为此画的透明图来表达了
效果图:
左边原图,右边替换后的透明图
思考:这样确实相当干净,但是它是递归函数,一旦遇到大的图就会耗尽栈的内存,立刻就程序出错了,这该怎么办?
答:我还是有办法,下一篇博客将会展示一种更好的非递归算法,用循环来达到这种效果,完全没有问题。
这是个算法,也是个人自己写的,当时在写桌宠的时候以为很有效,结果换了个大点的图立刻使得我的程序崩溃,而在半夜,我突然想出了一种新的循环代替递归的算法,这会在下篇博客写出来。
如今初入DSDN,将解决的算法一步步的分享出来,请各位多多包涵,有文章逻辑或排版问题还请指出。