更改图像色调的更快方法?

发布于 2024-11-27 23:40:39 字数 1343 浏览 2 评论 0原文

当我在滑动条上滑动时,是否有更好的方法可以平滑地更改图像的色调?我正在做的是获取每个像素的 RGB 值,计算饱和度和亮度,然后将这些值放入。图像的大小会影响色调更新的速度。 Photoshop 的图像色调功能可以将色调从 0 平滑地更改为 360,这正是我想要尝试并达到的效果。

data = editImage.LockBits(new Rectangle(0, 0, editWidth, editHeight), 
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
byte* dataPtr = (byte*)data.Scan0;
h = trackBar1.Value / 60.0;
D = 1 - Math.Abs((h % 2) - 1);

if (h >= 0 && h < 1)
{
    for (int i = 0; i < editHeight; i++)
    {
        offsetStride = i * data.Stride;
        for (int j = 0; j < editWidth; j++)
        {
            blue = dataPtr[(j * 3) + offsetStride];
            green = dataPtr[(j * 3) + offsetStride + 1];
            red = dataPtr[(j * 3) + offsetStride + 2];

            if (green > blue) max = green;
            else max = blue;
            if (red > max) max = red;

            if (green < blue) min = green;
            else min = blue;
            if (red < min) min = red;

            s = (max == 0) ? 0 : 1d - (1d * min / max);
            v = max / 255d;

            C = v * s;
            X = C * D;
            E = v - C;

            dataPtr[(j * 3) + offsetStride] = (byte)(min);
            dataPtr[(j * 3) + offsetStride + 1] = (byte)((X + E) * 255);
            dataPtr[(j * 3) + offsetStride + 2] = (byte)(max);
        }
    }
}

Is there a better method to change the hue of an image smoothly as I slide through a trackbar? What I'm doing is getting the RGB value per pixel, calculating the saturation and brightness then putting the values in. The size of the image affects the speed the hue is updated. Photoshop's image hue function changes the hue from 0 to 360 smoothly which is what I want to try and get to.

data = editImage.LockBits(new Rectangle(0, 0, editWidth, editHeight), 
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
byte* dataPtr = (byte*)data.Scan0;
h = trackBar1.Value / 60.0;
D = 1 - Math.Abs((h % 2) - 1);

if (h >= 0 && h < 1)
{
    for (int i = 0; i < editHeight; i++)
    {
        offsetStride = i * data.Stride;
        for (int j = 0; j < editWidth; j++)
        {
            blue = dataPtr[(j * 3) + offsetStride];
            green = dataPtr[(j * 3) + offsetStride + 1];
            red = dataPtr[(j * 3) + offsetStride + 2];

            if (green > blue) max = green;
            else max = blue;
            if (red > max) max = red;

            if (green < blue) min = green;
            else min = blue;
            if (red < min) min = red;

            s = (max == 0) ? 0 : 1d - (1d * min / max);
            v = max / 255d;

            C = v * s;
            X = C * D;
            E = v - C;

            dataPtr[(j * 3) + offsetStride] = (byte)(min);
            dataPtr[(j * 3) + offsetStride + 1] = (byte)((X + E) * 255);
            dataPtr[(j * 3) + offsetStride + 2] = (byte)(max);
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我们只是彼此的过ke 2024-12-04 23:40:39

如果您想要改变色调的单色图像,那么最有效的方法是生成红色、黄色、绿色、青色、蓝色和洋红色图像,并根据需要在它们之间进行插值。

因此,要生成旋转 10% 的图像,您需要添加 50/60 * 红色 + 10/60 * 黄色并显示它。

事实上,您并不需要生成全部 6 张图像。生成红色和黄色图像就足够了。您可以即时生成其他图像,因为例如,蓝色图像只是红色图像,但交换了红色和蓝色像素(您甚至可以从红色导出黄色)。但最简单的做法可能是从 6 个图像开始,然后再添加优化。

生成一个初始彩色图像的代码与上面的代码类似(乍一看),但是从 HSV 到 RGB 的最后一步看起来有点可疑 - 维基百科页面很好地解释了这一点(并且 C# 可能包含执行此转换的库函数)。

[鉴于您的澄清,下面与您正在做的事情无关]

如果您想逐个像素地改变色调(因此红色移动到黄色,绿色移动到青色),那么您可以通过首先转换整个图像来节省一些时间HSV,然后以此为基础进行工作。

通过生成多个图像(例如色调每旋转 10 度)然后在它们之间进行插值,您可以节省更多时间(在改变色调时)。但它比单色情况更复杂,因为某些像素(在插值过程中色调移动 60 度的倍数的像素)需要特殊处理。因此,您还需要预先检测这些,然后动态计算它们。这是一个权衡,因为你拥有的中间图像越多,特殊情况下需要的像素就越少。在实践中,惰性地(按需)生成单独的图像将提供更快的启动速度,因为通常人们只进行微小的更改。

If you want a monochrome image that changes hue then the most efficient method would be to generate red, yellow, green, cyan, blue and magenta images and interpolate between them as necessary.

So to generate an image at 10% rotation you would add 50/60 * red + 10/60 * yellow and display that.

In fact, you don't really need to generate all 6 images. It would be enough to generate the red and yellow images. The others you can generate on the fly, because, for example, the blue image is just the red image, but with the red and blue pixels swapped (and you could even derive the yellow from the red). But it's perhaps simplest to start with 6 images and then add that optimisation later.

The code to generate one of the initial coloured images is similar to what you have above (at first glance), but your final step from HSV to RGB looks a little suspect - the wikipedia pages are good at explaining this (and it's possible C# contains a library function to do this conversion).

[Given your clarification, below is not relevant to what you are doing]

If you want to shift the hue, pixel by pixel (so reds move to yellow as greens move to cyan) then you would save some time by first converting the entire image to HSV, and then working from that.

You might save more time (when shifting hue) by generating multiple images (say every 10 degrees rotated in hue) and then interpolating between them. But it's more complex than the monochrome case, because some pixels (those for which hue moves through a multiple of 60 degrees during the interpolation) need special treatment. So you also need to pre-detect those and then calculate them on the fly. There's a trade-off, in that the more intermediate images you have, the less pixels you need to special-case. And in practice generating the separate images lazily (on demand) is going to give quicker startup, because typically people only make small changes.

孤凫 2024-12-04 23:40:39

您可以尝试使用查找表来摆脱“s”的计算,因为 min 和 max 的范围只能从 0 到 255。因此您将得到 s = getS(min, max) 。 C 和 E 的值可能也可以保存在此表中(但已经晚了,我还没有那么努力地查看)。

getS 表的每个元素都将被预先计算,就像

for (int min = 0; min < 255; min++)
{
    for (int max = 0; max < 255; max++)
    {
        tableS[min, max] = (max == 0) ? 0 : 1d - (1d * min / max);
    }
}

Photoshop 的代码也可能用汇编程序编写一样,以获得最大性能。

为了看看这是否会产生很大的影响,您可以注释掉所有色调计算代码,并通过复制假数据来查看它的执行情况。如果它像 Photoshop 一样快速,那么您就知道这些计算是您的瓶颈。

You could try using a lookup table to get rid of the calculation of 's', since min and max can only range from 0 to 255. So you'd have s = getS(min, max). The values of C and E could probably also be held in this table (but it's late and I haven't looked that hard).

Each element of the getS table would be precomputed like

for (int min = 0; min < 255; min++)
{
    for (int max = 0; max < 255; max++)
    {
        tableS[min, max] = (max == 0) ? 0 : 1d - (1d * min / max);
    }
}

Photoshop's code is probably written in assembler too to get maximum performance.

In order to see if this will have much effect you could comment out all the Hue calculation code and see how it performs just by copying fake data. If it's as zippy as Photoshop then you know these calculations are your bottleneck.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文