将 BMP 图像转换为绘图仪的指令集?
Converting BMP image to set of instructions for a plotter?
我有这样一个绘图仪:
我必须执行的任务是将 24 位 BMP 转换为该绘图仪的指令集。在绘图仪中,我可以更改 16 种常见颜色。我面临的第一个复杂问题是颜色减少。我面临的第二个复杂问题是如何将像素转换为一组绘图指令。
作为绘图工具,将使用带有油画颜料的画笔。这意味着绘图仪的绘图线不会那么细,而且会比较短。
请推荐可用于解决此图像数据转换问题的算法?
一些初步结果:
抖动
好吧,我今天有时间做这件事,结果就在这里。您没有提供您的绘图仪调色板,所以我从您生成的图像中提取了它,但您可以使用任何调色板。抖动背后的想法很简单,我们的感知在区域而不是单个像素上集成颜色,因此您必须使用一些颜色差异的累加器来代替渲染的内容和应该渲染的内容,并将其添加到下一个像素...
这样,该区域的颜色大致相同,但实际只使用离散数量的颜色。如何更新此信息的形式可以将结果分支抖动区分为多种方法。简单明了是这样的:
- 将颜色累加器重置为零
- 处理所有像素
- 将每个像素的颜色添加到累加器
- 在您的调色板中找到最接近结果的匹配项
- 渲染所选调色板颜色
- 从累加器中减去选定的调色板颜色
这是你的输入图片(我把它们放在一起了):
此处为您的来源结果图片:
左上角的颜色方块只是我使用的调色板(从您的图像中提取)。
这里是代码 (C++) 我用:
picture pic0,pic1,pic2;
// pic0 - source img
// pic1 - source pal
// pic2 - output img
int x,y,i,j,d,d0,e;
int r,g,b,r0,g0,b0;
color c;
List<color> pal;
// resize output to source image size clear with black
pic2=pic0; pic2.clear(0);
// create distinct colors pal[] list from palette image
for (y=0;y<pic1.ys;y++)
for (x=0;x<pic1.xs;x++)
{
c=pic1.p[y][x];
for (i=0;i<pal.num;i++) if (pal[i].dd==c.dd) { i=-1; break; }
if (i>=0) pal.add(c);
}
// dithering
r0=0; g0=0; b0=0; // no leftovers
for (y=0;y<pic0.ys;y++)
for (x=0;x<pic0.xs;x++)
{
// get source pixel color
c=pic0.p[y][x];
// add to leftovers
r0+=WORD(c.db[picture::_r]);
g0+=WORD(c.db[picture::_g]);
b0+=WORD(c.db[picture::_b]);
// find closest color from pal[]
for (i=0,j=-1;i<pal.num;i++)
{
c=pal[i];
r=WORD(c.db[picture::_r]);
g=WORD(c.db[picture::_g]);
b=WORD(c.db[picture::_b]);
e=(r-r0); e*=e; d =e;
e=(g-g0); e*=e; d+=e;
e=(b-b0); e*=e; d+=e;
if ((j<0)||(d0>d)) { d0=d; j=i; }
}
// get selected palette color
c=pal[j];
// sub from leftovers
r0-=WORD(c.db[picture::_r]);
g0-=WORD(c.db[picture::_g]);
b0-=WORD(c.db[picture::_b]);
// copy to destination image
pic2.p[y][x]=c;
}
// render found palette pal[] (visual check/debug)
x=0; y=0; r=16; g=pic2.xs/r; if (g>pal.num) g=pal.num;
for (y=0;y<r;y++)
for (i=0;i<g;i++)
for (c=pal[i],x=0;x<r;x++)
pic2.p[y][x+(i*r)]=c;
其中 picture
是我的图片 class 所以这里有一些成员:
xs,ys
分辨率
color p[ys][xs]
直接像素访问(32 位像素格式,每个通道 8 位)
clear(DWORD c)
用颜色 c
填充图像
对于简单的频道访问,color
只是 DWORD dd
和 BYTE db[4]
的 union
。
List<>
是我的模板(动态array/list>
List<int> a
等同于 int a[]
.
add(b)
在列表末尾添加 b
num
是列表中的项目数
现在为了避免点太多(为了您的绘图仪的使用寿命),您可以使用不同的线条图案等,但这需要很多 trial/error ...例如,您可以数出多少次在某些区域使用了一种颜色,并且根据该比例使用不同的填充图案(基于线条)。您需要在图像质量和 rendering/durability ...
的速度之间做出选择
如果没有关于您的绘图仪功能(速度、工具更换方法、颜色组合行为)的更多信息,则很难确定形成控制流的最佳方法。我打赌你手动更改颜色,这样你就可以一次渲染每种颜色。因此,提取所有具有第一个工具颜色的像素,将相邻像素合并到 lines/curves 并渲染...然后移动到下一个工具颜色...
我有这样一个绘图仪:
我必须执行的任务是将 24 位 BMP 转换为该绘图仪的指令集。在绘图仪中,我可以更改 16 种常见颜色。我面临的第一个复杂问题是颜色减少。我面临的第二个复杂问题是如何将像素转换为一组绘图指令。
作为绘图工具,将使用带有油画颜料的画笔。这意味着绘图仪的绘图线不会那么细,而且会比较短。
请推荐可用于解决此图像数据转换问题的算法?
一些初步结果:
抖动
好吧,我今天有时间做这件事,结果就在这里。您没有提供您的绘图仪调色板,所以我从您生成的图像中提取了它,但您可以使用任何调色板。抖动背后的想法很简单,我们的感知在区域而不是单个像素上集成颜色,因此您必须使用一些颜色差异的累加器来代替渲染的内容和应该渲染的内容,并将其添加到下一个像素...
这样,该区域的颜色大致相同,但实际只使用离散数量的颜色。如何更新此信息的形式可以将结果分支抖动区分为多种方法。简单明了是这样的:
- 将颜色累加器重置为零
- 处理所有像素
- 将每个像素的颜色添加到累加器
- 在您的调色板中找到最接近结果的匹配项
- 渲染所选调色板颜色
- 从累加器中减去选定的调色板颜色
这是你的输入图片(我把它们放在一起了):
此处为您的来源结果图片:
左上角的颜色方块只是我使用的调色板(从您的图像中提取)。
这里是代码 (C++) 我用:
picture pic0,pic1,pic2;
// pic0 - source img
// pic1 - source pal
// pic2 - output img
int x,y,i,j,d,d0,e;
int r,g,b,r0,g0,b0;
color c;
List<color> pal;
// resize output to source image size clear with black
pic2=pic0; pic2.clear(0);
// create distinct colors pal[] list from palette image
for (y=0;y<pic1.ys;y++)
for (x=0;x<pic1.xs;x++)
{
c=pic1.p[y][x];
for (i=0;i<pal.num;i++) if (pal[i].dd==c.dd) { i=-1; break; }
if (i>=0) pal.add(c);
}
// dithering
r0=0; g0=0; b0=0; // no leftovers
for (y=0;y<pic0.ys;y++)
for (x=0;x<pic0.xs;x++)
{
// get source pixel color
c=pic0.p[y][x];
// add to leftovers
r0+=WORD(c.db[picture::_r]);
g0+=WORD(c.db[picture::_g]);
b0+=WORD(c.db[picture::_b]);
// find closest color from pal[]
for (i=0,j=-1;i<pal.num;i++)
{
c=pal[i];
r=WORD(c.db[picture::_r]);
g=WORD(c.db[picture::_g]);
b=WORD(c.db[picture::_b]);
e=(r-r0); e*=e; d =e;
e=(g-g0); e*=e; d+=e;
e=(b-b0); e*=e; d+=e;
if ((j<0)||(d0>d)) { d0=d; j=i; }
}
// get selected palette color
c=pal[j];
// sub from leftovers
r0-=WORD(c.db[picture::_r]);
g0-=WORD(c.db[picture::_g]);
b0-=WORD(c.db[picture::_b]);
// copy to destination image
pic2.p[y][x]=c;
}
// render found palette pal[] (visual check/debug)
x=0; y=0; r=16; g=pic2.xs/r; if (g>pal.num) g=pal.num;
for (y=0;y<r;y++)
for (i=0;i<g;i++)
for (c=pal[i],x=0;x<r;x++)
pic2.p[y][x+(i*r)]=c;
其中 picture
是我的图片 class 所以这里有一些成员:
xs,ys
分辨率color p[ys][xs]
直接像素访问(32 位像素格式,每个通道 8 位)clear(DWORD c)
用颜色c
填充图像
对于简单的频道访问,color
只是 DWORD dd
和 BYTE db[4]
的 union
。
List<>
是我的模板(动态array/list>
List<int> a
等同于int a[]
.add(b)
在列表末尾添加 bnum
是列表中的项目数
现在为了避免点太多(为了您的绘图仪的使用寿命),您可以使用不同的线条图案等,但这需要很多 trial/error ...例如,您可以数出多少次在某些区域使用了一种颜色,并且根据该比例使用不同的填充图案(基于线条)。您需要在图像质量和 rendering/durability ...
的速度之间做出选择如果没有关于您的绘图仪功能(速度、工具更换方法、颜色组合行为)的更多信息,则很难确定形成控制流的最佳方法。我打赌你手动更改颜色,这样你就可以一次渲染每种颜色。因此,提取所有具有第一个工具颜色的像素,将相邻像素合并到 lines/curves 并渲染...然后移动到下一个工具颜色...