当前位置: 首页 > 面试题库 >

根据某些给定的参考元素对颜色矩阵进行插值

宓季同
2023-03-14
问题内容

这或多或少是对从4种角落颜色插值的二维颜色渐变(256x256矩阵)的后续问题,今天的jadsq对此做出了深刻的回答。

对于线性渐变,先前的答案效果很好。但是,如果希望更好地控制渐变的终止色,则此方法似乎不太实用。在这种情况下可能会有所帮助的是在矩阵(查找表)中具有一些参考色点,这些参考色点用于为查找表中的空白位置插入颜色值。我的意思可能更容易从下面的图像中读出。

整个想法取材于http://cartography.oregonstate.edu/pdf/2006_JennyHurni_SwissStyleShading.pdf第4至6页。我已经阅读了这篇论文,理论上理解了正在发生的事情,但是由于我的经验不足而失败了内插法和说实话的一般数学技巧。可能还感兴趣的是,它们使用S型高斯钟形作为插值方法(第6页)。他们认为高斯加权产生了视觉上最好的结果,并且易于计算(等式1,对于每256个单元格中的256个表,k
= 0.0002)。

我已经使用了它们介绍的方法的其他部分,但是将空值填充到矩阵中确实是关键部分,这使我无法继续。再次感谢您的帮助!

我现在所拥有的:

#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt

# the matrix with the reference color elements
ref=np.full([7, 7, 3], [255,255,255], dtype=np.uint8)
ref[0][6] = (239,238,185)
ref[1][1] = (120,131,125)
ref[4][6] = (184,191,171)
ref[6][2] = (150,168,158)
ref[6][5] = (166,180,166)

# s = ref.shape
#
# from scipy.ndimage.interpolation import zoom
# zooming as in https://stackoverflow.com/a/39485650/1230358 doesn't seem to work here anymore, because we have no corner point as reference but randomly distributed points within the matrix. As far as I know ...
# zoomed=zoom(ref,(256/s[0],256/s[1],1),order=1)

plt.subplot(211)
plt.imshow(ref,interpolation='nearest')
# plt.subplot(212)
# plt.imshow(zoomed,interpolation='nearest')
plt.show()

问题答案:

首先是一些问题,以更好地阐明您的问题:

  • 您想要哪种插值:线性/三次/其他?
  • 有哪些要点约束?例如,这些控制点是否总是会封装单个区域,或者内部也可能存在点?

对于简单的线性插值和任意(但至少3个点不在一行上),我将尝试这样做:

  1. 三角控制点区域

覆盖整个定义区域的非重叠三角形。

  1. 渲染三角形

因此,只需栅格化,请参见算法以填充三角形和所有子链接。您还应该R,G,B与坐标一起插值。

  1. 创建2个渐变副本,用H线外推一个,用V线外推一个

因此,扫描梯度的所有H水平线,如果发现2个已知像素彼此相距足够远(例如,梯度大小的四分之一或一半),则外推整条线的未知颜色。因此,如果找到已知端点(红色),(x0,y,r0,g0,b0),(x1,y,r1,g1,b1)则在同一行中将所有未知颜色设置为:

    r = r0+(r1-r0)*(x-x0)/(x1-x0)
g = g0+(g1-g0)*(x-x0)/(x1-x0)
b = b0+(b1-b0)*(x-x0)/(x1-x0)

类似地,现在在V垂直线的渐变副本中执行相同的操作。所以点现在是(x,y0,r0,g0,b0),(x,y1,r1,g1,b1)`和外推法:

    r = r0+(r1-r0)*(y-y0)/(y1-y0)
g = g0+(g1-g0)*(y-y0)/(y1-y0)
b = b0+(b1-b0)*(y-y0)/(y1-y0)

在此之后,比较两个副本,如果同时计算了两个未知点,则将其设置为目标渐变图像中两种颜色的平均值。循环整个过程( #3
),直到没有添加新的渐变像素为止。

  1. 其余使用单一外推颜色

根据定义控制点的方式,某些区域将只有一种外推颜色(来自H或V线,但不能同时来自两者),因此仅对这些区域使用一种计算出的颜色(在完成 #3 之后)。

如果您想要简单(而不是精确)的东西,则可以将已知控制点颜色(使用平滑滤镜)渗色到相邻像素,直到整个渐变被填充和饱和为止。

  1. 用未计算的预定义颜色填充未知渐变像素
  2. 将每个像素设置为其计算的邻居的平均值

您可以在单独的图像中执行此操作以避免移动。

  1. 将控制点设置回原始颜色

  2. 循环#2,直到区域填充/饱和/或预定义的迭代次数

[Edit1]第二个解决方案

好的,我将 C / C 与您的点/颜色和渐变大小放在一起,这就是它的样子(我流血了100次,没有邻居的4个邻居出血)

左边的图像是输入矩阵,如果像素是参考点,则将其编码为alpha通道(最高8位),或者尚未定义。右图是应用出血100次后的图像。出血很简单,只需获取任何非参考点并将其重新计算为周围所有可用像素及其自身的平均值(忽略任何未定义的颜色)。

在这里,您可以忽略要渲染的 GDI* 内容的 C ++ 代码(请注意,我的渐变贴图首先具有坐标!) *x``y

//---------------------------------------------------------------------------
const int mxs=7,mys=7,msz=16;   // gradient resolution x,y and square size for render
DWORD map[mxs][mys];            // gradient matrix ... undefined color is >= 0xFF000000
// 0x00?????? - reference color
// 0xFF?????? - uncomputed color
// 0xFE?????? - bleeded color
//---------------------------------------------------------------------------
void map_clear()    // set all pixels as uncomputed (white with alpha=255)
    {
    int x,y;
    for (x=0;x<mxs;x++)
     for (y=0;y<mys;y++)
      map[x][y]=0xFFFFFFFF;
    }
void map_bleed()    // bleed computed colors
    {
    int x,y,r,g,b,n;
    DWORD tmp[mxs][mys],c;
    for (x=0;x<mxs;x++)
     for (y=0;y<mys;y++)
        {
        c=map[x][y];
        n=0; r=0; g=0; b=0; if (DWORD(c&0xFF000000)==0) { tmp[x][y]=c; continue; }      if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; }
        x++;      if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; }
        x--; y--; if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; }
        x--; y++; if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; }
        x++; y++; if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; }
        y--;      if (!n) { tmp[x][y]=0xFFFFFFFF; continue; }
        c=((r/n)|((g/n)<<8)|((b/n)<<16))&0x00FFFFFF;
        tmp[x][y]=c;
        }
    // copy tmp back to map
    for (x=0;x<mxs;x++)
     for (y=0;y<mys;y++)
      map[x][y]=tmp[x][y];
    }
void map_draw(TCanvas *can,int x0,int y0)   // just renders actual gradient map onto canvas (can ignore this)
    {
    int x,y,xx,yy;
    for (x=0,xx=x0;x<mxs;x++,xx+=msz)
     for (y=0,yy=y0;y<mys;y++,yy+=msz)
        {
        can->Pen->Color=clBlack;
        can->Brush->Color=map[x][y]&0x00FFFFFF;
        can->Rectangle(xx,yy,xx+msz,yy+msz);
        }
    }
//---------------------------------------------------------------------------

这是用法(您的示例):

// clear backbuffer
bmp->Canvas->Brush->Color=clBlack; 
bmp->Canvas->FillRect(TRect(0,0,xs,ys));

// init your gradient with reference points
map_clear();
//  x  y       R     G        B
map[6][0] = (239)|(238<<8)|(185<<16);
map[1][1] = (120)|(131<<8)|(125<<16);
map[6][4] = (184)|(191<<8)|(171<<16);
map[2][6] = (150)|(168<<8)|(158<<16);
map[5][6] = (166)|(180<<8)|(166<<16);
map_draw(bmp->Canvas,msz,msz); // render result (left)
// bleed
for (int i=0;i<100;i++) map_bleed();
map_draw(bmp->Canvas,(mxs+2)*msz,msz); // render result (right)

// refresh window with backbufer (anti-flickering)
Main->Canvas->Draw(0,0,bmp);

同样,您可以忽略所有渲染内容。出血的数量应比对角像素大2倍,以便渗色覆盖所有像素。例如,迭代次数越多,我尝试的结果越饱和100,结果看起来也不错..所以我不再使用它了…

[Edit2],这里是第二种方法的算法

  1. 将标志添加到插值矩阵

您需要知道像素是否为reference,undefinedinterpolated。您可以将此编码为alpha通道,也可以使用蒙版(单独的2D矩阵)。

  1. 渗出/平滑基质

基本上,对于每个非reference像素,将其新值计算为undefined周围(4/8个邻居)及其位置上所有非像素的平均值。请勿使用undefined像素并将计算值存储到临时矩阵(不要弄乱下一个像素,否则渗色/平滑化通常会沿对角线方向移动像素)。这样,未定义的像素区域将缩小1个像素。完成整个矩阵后,将临时矩阵的内容复制到原始矩阵(或交换指针)中。

  1. 循环2,直到结果达到饱和或特定的迭代次数

计数的数量应至少是对角像素数量的2倍,以将参考像素传播到整个矩阵中。可以在将临时数组复制到原始数组中的同时在 #2中
进行饱和度检查(可以在帧之间进行绝对差异,如果零或接近零则停止)。



 类似资料:
  • 我有以下问题: 我有一个矩阵。现在,我想在矩阵的每一行中删除一个条目:在包含某个数字(比如4)的行中,我想删除带有该数字的条目,在其他行中,我只想删除最后一个元素。 如果我有矩阵 这给了 2 0 4 0 删除后应该只是 2. 0 谢谢你的帮助!

  • 考虑矩阵: 然后,列表: 如果badcombos矩阵中的任何颜色组合出现在列表中(即上例中的子列表[[3]]和[[4]]),则将从列表中删除,我如何根据这些条件“减少”列表。

  • 问题内容: 我正在尝试编写一种算法,用于在给定的子矩阵中查找子矩阵。为了解决这个问题,我编写了以下代码: 这段代码可以正常工作,但是我不确定这是问题的确切解决方案还是可以解决。请提供您的专家意见。提前致谢。 问题答案: 该算法对4×4矩阵和2×2子矩阵进行了硬编码。否则,它看起来像蛮力算法。 我会这样表示: 如果您想要更有效的方法,建议您将它们压扁,如下所示: 并在此序列中搜索以下模式: 使用标准

  • 问题内容: 对于给定的多色PNG (具有透明度),什么是最好的/快速惯用的方法: 创建一个副本 在副本中找到所有黑色像素并将其更改为红色 (返回修改后的副本) 关于SO有一些相关的问题,但我一直无法找到可行的方法。 问题答案: 您必须提取图像的像素缓冲区,然后可以循环浏览,并根据需要更改像素。最后,从缓冲区创建一个新图像。 在Swift 3中,这看起来像:

  • 对于给定的多色PNG(带透明度),最好的/快速的惯用方式是什么: 创建一个重复的 关于SO有几个相关的问题,但是我还没有找到有效的方法。

  • 下表显示了HTML 3.2中引入的16种颜色名称,以支持8位图形卡提供的16种颜色。 CSS中可以使用相同的颜色集 - 颜色名称 十六进制值 颜色 节目 aqua #00ffff 00ffff black #000000 000000 blue #0000ff 0000ff fuchsia #ff00ff ff00ff green #008000 008000 gray #808080 80808