【问题标题】:How to use LUT with JavaScript?如何在 JavaScript 中使用 LUT?
【发布时间】:2014-09-14 14:56:41
【问题描述】:

我是图像处理的新手。

我想使用 JavaScript 使用 LUT(查找表)或相应的查找 PNG 将效果应用于图像,如下所示:

我在 Google 上搜索了很多,但找不到一篇文章或任何资源来描述使用 LUT 进行像素转换的确切过程。

我找到了一篇不错的文章 here,它描述了 1D 和 3D LUT 以及它们之间的区别。但它对我来说仍然不完全清楚。

我想要类似this 的东西,它是为 iOS 完成的。

附:请不要发布有关图像过滤库的链接/答案,这些库将卷积矩阵用于效果或过滤器。

更新:

终于!感谢@abbath,我得到了答案。我在 GitHub 上创建了一个 gist,你可以找到 here

【问题讨论】:

    标签: javascript image-processing lookup-tables


    【解决方案1】:

    我也是这个话题的新手,但我希望它对我目前所发现的有所帮助。您的图像(LUT)是 3D 数组的表示,想象一个 64x64x64 的立方体。此表示是 2D 的,一个平面是 64x64 正方形。你需要这个平面的 64 块,这就是 png 中有 8x8 个正方形的原因。 如果你仔细看,左上角的正方形在 X 轴上有红色(RGB 的 R),在 Y 轴上有绿色(G)。蓝色 (B) 是 Z 轴(我想它是向上的),它不能用 2D 表示,所以它出现在接下来的 64x64 方格上。在最后一个(右下)方格中,您可以看到它大部分是蓝色的,其中红色和绿色坐标为 0。

    所以下一个问题是如何使用这个 LUT 投影图像。我已经在 J​​ava 中尝试过,在我看来,您将不得不丢失一些信息,因为 64x64x64 远小于 256x256x256,在这种情况下,您必须将每个像素颜色值除以 4。

    步骤:

    1. 遍历原始图像的像素。在一次迭代中,您将拥有原始图像的一个像素。获取该像素的 R、G、B 值。
    2. 将这些值除以 4,得到小于 64 的值。
    3. 从 LUT 中获取相应的像素,就像它是 64x64x64 立方体一样。所以如果RGB=(255,0,255)在原始像素上,除法得到(63,0,63),然后检查B值,得到png上对应的正方形,就是右下角(在 b=63 的情况下),并获取它的 r,g=(63,0) 像素,这在您的 png 图像上呈紫色。

    我现在用java代码检查了android,我认为它足够快。下面是我写的代码,我希望它足以将它移植到javascript。 (希望cmets足够理解)

    public Bitmap applyEffectToBitmap(Bitmap src, Bitmap lutbitmap) {
        //android specific code, storing the LUT image's pixels in an int array
        int lutWidth = lutBitmap.getWidth();
        int lutColors[] = new int[lutWidth * lutBitmap.getHeight()];
        lutBitmap.getPixels(lutColors, 0, lutWidth, 0, 0, lutWidth, lutBitmap.getHeight());
    
        //android specific code, storing the source image's pixels in an int array
        int srcWidth = src.getWidth();
        int srcHeight = src.getHeight();
        int[] pix = new int[srcWidth * srcHeight];
    
        src.getPixels(pix, 0, srcWidth, 0, 0, srcWidth, srcHeight);
    
        int R, G, B;
        //now we can iterate through the pixels of the source image
        for (int y = 0; y < srcHeight; y++){
            for (int x = 0; x < srcWidth; x++) {
                //index: because the pix[] is one dimensional
                int index = y * srcWidth+ x;
                //pix[index] returns a color, we need it's r g b values, thats why the shift operator is used
                int r = ((pix[index] >> 16) & 0xff) / 4;
                int g = ((pix[index] >> 8) & 0xff) / 4;
                int b = (pix[index] & 0xff) / 4;
    
                //the magic happens here: the 3rd step above: blue pixel describes the 
                //column and row of which square you'll need the pixel from
                //then simply add the r and green value to get the coordinates 
                int lutX = (b % 8) * 64 + r;
                int lutY = (b / 8) * 64 + g;
                int lutIndex = lutY * lutWidth + lutX;
    
    
                //same pixel getting as above, but now from the lutColors int array
                R = ((lutColors[lutIndex] >> 16) & 0xff);
                G = ((lutColors[lutIndex] >> 8) & 0xff);
                B = ((lutColors[lutIndex]) & 0xff);
    
    
                //overwrite pix array with the filtered values, alpha is 256 in my case, but you can use the original alpha
                pix[index] = 0xff000000 | (R << 16) | (G << 8) | B;
            }
        }
        //at the end of operations pix[] has the transformed pixels sou can set them to your new bitmap
        //some android specific code is here for creating bitmaps
        Bitmap result = Bitmap.createBitmap(srcWidth, srcHeight, src.getConfig());
        result.setPixels(pix, 0, srcWidth, 0, 0, srcWidth, srcHeight);
        return result ;
    }
    

    现在我也成功地用 javascript 实现了,检查是否使用了 Math.floor() 函数:

    for (var i=0;i<imgData.data.length;i+=4){
        var r=Math.floor(imgData.data[i]/4);
        var g=Math.floor(imgData.data[i+1]/4);
        var b=Math.floor(imgData.data[i+2]/4);    
        var a=255;
    
    
        var lutX = (b % 8) * 64 + r;
        var lutY = Math.floor(b / 8) * 64 + g;
        var lutIndex = (lutY * lutWidth + lutX)*4;
    
        var Rr =  filterData.data[lutIndex];
        var Gg =  filterData.data[lutIndex+1];    
        var Bb =  filterData.data[lutIndex+2];
    
        imgData.data[i] = Rr;
        imgData.data[i+1] = Gg;
        imgData.data[i+2] = Bb;
        imgData.data[i+3] = 255;
    
    }
    

    在这里查看:http://jsfiddle.net/gxu080ve/1/ lut 图像是字节码,抱歉。

    此代码仅适用于 64x64x64 3DLUT 图像。如果您的 LUT 有其他尺寸,参数会有所不同; / 4* 64% 8/ 8 必须更改为其他尺寸,但在这个问题的范围内,LUT 是 64x64x64。

    【讨论】:

    • 感谢您的回答。我有一些问题:你说Get the corresponding pixel from your LUT是什么意思?如果我的图像比 LUT 大怎么办?如何check the B value?在这些步骤之后,我将如何使用结果 (63, 0) 更改图像的当前像素?
    • 您的图像可以有任何大小,您必须遍历图像的每个像素。因此,如果您有全高清图像,则必须遍历 1080*1920 像素。在一次迭代中,您将拥有一个像素值。获取该像素的 r、g 和 b 值。它们将小于 256,但您将需要小于 64,因此将所有三个值除以 4。所以现在您有一个像素的三个值,每个值都低于 64。然后转到第 3 点,并获得相应的像素如上所述,来自您的 LUT 图像。我先用一些 java 代码更新我的答案,希望对您有所帮助。
    • 我已经用一些 java 代码 (android) 更新了我的答案,并重写了步骤以便更容易理解,我希望它在 javascript 中对你有所帮助。
    • 谢谢@abbath。我会尝试将其移植到 JavaScript 并通知您。
    • 好吧。我已经在 J​​avaScript 中实现了相同的功能并进行了尝试。结果显然是错误的。您是否测试过您的代码并按预期工作?您可以在此处查看结果:之前:imgur.com/0vcCTK9 和之后:imgur.com/XLcE9jd。我使用了上面的LUT png。您还可以在此处查看 JavaScript 代码:pastebin.com/b4Q2W5fM。也许我做错了什么。
    【解决方案2】:

    我正在尝试在 c++ 中做类似的事情。 3D LUT 有 3 列值,通常介于 0 和 1 之间。这些列分别是 r g b,它们表示值的映射。如果您打开其中一个大小为 nxnxn 的文件,您会将每个值读入其各自的 R 数组或 G 数组或 B 数组。然后要在内部重新创建映射,您将使用双/浮点键和值迭代并填充 rgb 映射

    for every b
        for every g
            for every r
                // r g and b are indices
                index = b * n^2 + g * n + r
                Rmap.push((r + 1)/n, Rarray[index])
                // Same for g and b
                Gmap.push((g + 1)/n, Garray[index]
                Bmap.push((b + 1)/n, Barray[index]
    

    获得映射后,您可以将查找应用到图像中的每个像素。假设图像的最大颜色为 255,因此您采用每个像素的 (r,g,b)/255 并应用查找,然后按 *255 转换回来。据我所知,计算无法查找的值的惯例是执行三线性插值。我还没有取得太大的成功,但这是我的两分钱。

    【讨论】:

      猜你喜欢
      • 2014-02-03
      • 2015-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多