c# - 在 Windows 操作系统上将屏幕转为灰度

标签 c# winapi

我们能否在 Windows 操作系统 (7,8,10) 上使用 GammaRamp ( SetDeviceGammaRamp) 将屏幕变成灰度? 我需要这个来模拟电子墨水阅读器。 我正在使用此类来控制色温,并尝试使用以下因素实现将 RGB 图像转换为灰度的算法:红色 * 0.2126;绿色 * 0.7152;蓝色 * 0.0722 我在这个 article 中读到的. 结果不是纯灰度。我不明白 SetDeviceGammaRamp 设置的 gammaramp 数组究竟是如何改变颜色的,这就是为什么不能实现灰度算法的原因。关于如何使用 SetDeviceGammaRamp 在此类中实现灰度转换的任何建议?

public static class GammaRamp
    {
        [DllImport("gdi32.dll")]
        private unsafe static extern bool SetDeviceGammaRamp(Int32 hdc, ushort* ramp);

        [DllImport("gdi32")]
        private unsafe static extern bool GetDeviceGammaRamp(Int32 hdc, ushort* ramp);

        private static Int32 hdc;

        public static unsafe void Set(int aBrightness, int aRed, int aGreen, int aBlue)
        {
            double red = 1, green = 1, blue = 1;

            red = (double)aRed / (double)255;
            green = (double)aGreen / (double)255;
            blue = (double)aBlue / (double)255;

            //Memory allocated through stackalloc is automatically free'd by the CLR.
            ushort* rgbArray = stackalloc ushort[768]; //3 * 256
            ushort* idx = rgbArray;

            for (int j = 0; j < 3; j++)
            {
                for (int i = 0; i < 256; i++)
                {
                    double arrayVal = (double)(i * (aBrightness + 128));

                    if (arrayVal > 65535)
                        arrayVal = (double)65535;

                    if (j == 0) //red
                        arrayVal = arrayVal * red * 0.2126;
                    else if (j == 1) //green
                        arrayVal = arrayVal * green * 0.7152;
                    else //blue
                        arrayVal = arrayVal * blue * 0.0722;

                    *idx = (ushort)arrayVal;
                    idx++;
                }
            }

            hdc = Graphics.FromHwnd(IntPtr.Zero).GetHdc().ToInt32();
            SetDeviceGammaRamp(hdc, rgbArray);
        }
}

如果无法使用我喜欢的 GammaRamp,因为它在 Win 7,8 和 10 中受支持,我将使用一个新功能,但仅适用于 Windows 10

enter image description here

但要从 WPF 应用程序控制此设置,我必须更改以下注册表项

Computer\HKEY_CURRENT_USER\Software\Microsoft\ColorFiltering\Active = 1 Computer\HKEY_CURRENT_USER\Software\Microsoft\ColorFiltering\FilterType = 0

我可以很容易地做到这一点,但是如何让 Windows 操作系统从注册表中刷新这个新设置?这也将是一个有用的答案

最后我会说我知道 MagSetColorEffect如果无法使用 GammaRamp 或其他选项,我将在 Windows 7 中使用 winAPI,但它是最后一个选项,因为它需要启用 Aero,这是一种限制。

最佳答案

不可能使用像 SetDeviceGammaRamp 这样的函数来制作灰度滤镜,因为它们分别作用于每个颜色 channel 。 lpRamp 参数设置视频卡用于将像素强度(如将保存在屏幕截图中)映射到传输强度(如放置在 VGA 连接器中的针脚上)的 LUT .这主要是一个遗留 API,不会影响屏幕截图、远程桌面或使用所有显卡。

要制作灰度滤色器,您必须从每个颜色 channel 获取数据,并将其混合在一起。或者,您可以应用加权函数,使生成的图像更准确地反射(reflect)人类感知。

diagram showing mixing of red, green, and blue color channels

您可以使用通过 MagSetFullscreenColorEffect 函数指定的比例因子(该函数适用于大多数运行 Windows 8 或更高版本的 PC,以及绝大多数 Windows 7 PC)。我目前无法访问 FCU 机器,但我怀疑设置中的新选项只是调用 MagSetFullscreenColorEffect

MagSetFullscreenColorEffect 采用颜色矩阵,它允许您转换 RGBX 的四空间。可以了解变换矩阵on MSDNmany other places . ColorMatrix Viewer ( GitHub ) 是测试和调整这些颜色矩阵的出色工具。

有关如何为此目的使用 MagSetFullscreenColorEffect 的示例,请参见下面的示例。

C++:

#include <magnification.h>
#pragma comment(lib, "magnification.lib")

float redScale = 0.2126f, greenScale = 0.7152f, blueScale = 0.0722f;
MAGCOLOREFFECT magEffectInvert =
{ 
    { 
        { redScale,   redScale,   redScale,   0.0f,  0.0f },
        { greenScale, greenScale, greenScale, 0.0f,  0.0f },
        { blueScale,  blueScale,  blueScale,  0.0f,  0.0f },
        { 0.0f,       0.0f,       0.0f,       1.0f,  0.0f },
        { 0.0f,       0.0f,       0.0f,       0.0f,  1.0f }
    }
};

MagInitialize();
if (!MagSetFullscreenColorEffect(&magEffectInvert))
{
    std::cout << "Failed " << GetLastError() << std::endl;
}
system("pause");
MagUninitialize();

C#:

using System;
using System.Runtime.InteropServices;

namespace ManagedColorPlayground
{
    using static NativeMethods;

    class Program
    {
        static void Main(string[] args)
        {
            float redScale = 0.2126f, greenScale = 0.7152f, blueScale = 0.0722f;
            var magEffectInvert = new MAGCOLOREFFECT {
                transform = new [] {
                    redScale,   redScale,   redScale,   0.0f,  0.0f,
                    greenScale, greenScale, greenScale, 0.0f,  0.0f,
                    blueScale,  blueScale,  blueScale,  0.0f,  0.0f,
                    0.0f,       0.0f,       0.0f,       1.0f,  0.0f,
                    0.0f,       0.0f,       0.0f,       0.0f,  1.0f
                }
            };

            MagInitialize();
            MagSetFullscreenColorEffect(ref magEffectInvert);
            Console.ReadLine();
            MagUninitialize();
        }
    }

    static class NativeMethods
    {
        const string Magnification = "Magnification.dll";

        [DllImport(Magnification, ExactSpelling = true, SetLastError = true)]
        public static extern bool MagInitialize();

        [DllImport(Magnification, ExactSpelling = true, SetLastError = true)]
        public static extern bool MagUninitialize();

        [DllImport(Magnification, ExactSpelling = true, SetLastError = true)]
        public static extern bool MagSetFullscreenColorEffect(ref MAGCOLOREFFECT pEffect);

        public struct MAGCOLOREFFECT
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public float[] transform;
        }
    }
}

MagSetFullscreenColorEffect 之前: color

MagSetFullscreenColorEffect 之后: grayscale

关于c# - 在 Windows 操作系统上将屏幕转为灰度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47369358/

相关文章:

c# - 使用 Entity Framework 检查日期列表中的数据库表中是否存在任何值

c# - Adorner 会破坏 MVVM 吗?

c# - 我如何测试忽略 NSubstitute 中的 params 参数的调用?

c++ - winAPI C++ 禁用无线适配器

c++ - 如何在 Rust winapi 编程中使用 COM VARIANT?

c# - 如何教 User.IsInRole

c# - 如何使用迁移手动创建 asp.net 身份表?

c - 我们如何将 FILE* 转换为 HANDLE?

c - 在c中操作LPCWSTR

c# - 如何在 Process.StandardOutput 中禁用输出缓冲