在上一篇:UWP/WinUI3 PixelShaderEffect 实现ThresholdEffect 滤镜。 - 吃饭/睡觉 - 博客园 (cnblogs.com) 已经价绍了如何编写hsls,编译,和使用 PixelShaderEffect 来实现自定义滤镜效果了,那么本编将介绍如何编写一个 “颜色替换滤镜”;
效果图:
一.颜色匹配理论
1.根据指定的颜色和阈值匹配到 指定颜色范围的像素。
2.利用hsv颜色模式 调整选中像素的 色调,饱和度,亮度;
//设置输入源数量
#define D2D_INPUT_COUNT 1
//将输入源0 设置为简单采样模式
#define D2D_INPUT0_SIMPLE
//引入hlsl帮助程序
#include "d2d1effecthelpers.hlsli"
//定义属性
//源颜色 float(0,0,0)~float(1,1,1)
float3 sourceColor;
//替换后的颜色,hsv颜色 float(0,0,0)~float(1,1,1)
float3 hsv;
//颜色容差 0~1
float threshold;
//HSV 到 RGB 转换
float3 hsv2rgb(float3 c)
{
float3 rgb = clamp(abs(fmod(c.x * 6.0 + float3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
float3 f1 = float3(1,1,1);
return c.z * lerp(f1, rgb, float3(c.y,c.y,c.y));
//return f1;
}
//rgb到hsv 转换
float3 rgb2hsv(float3 c)
{
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float a = step(c.b, c.g);
float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), float4(a,a,a,a));
float b = step(p.x, c.r);
float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), float4(b,b,b,b));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
//主程序
D2D_PS_ENTRY(main){
//获取当前像素颜色
float4 sColor = D2DGetInput(0);
float3 color = sColor.rgb;
//当前像素的每个分量减去输入颜色的对于分量的绝对值跟阈值做比较,查看是否符合条件
if(abs(color.r - sourceColor.r) < threshold && abs(color.g - sourceColor.g) < threshold && abs(color.b - sourceColor.b) < threshold)
{
//将当前颜色从 rgb转到hsv
float3 hsv = rgb2hsv(color);
//声明 计算后的hsv 变量
float3 thsv = float3(0,0,0);
//虽然这里用的都是rgb属性,但是对应的是hsv
//当前像素的hsv与输入的hsv相加
thsv.r= fmod(hsv.r*360+hsv.r*360,360)/360.0;
thsv.g= clamp(hsv.g+hsv.g,0,1);
thsv.b= clamp(hsv.b+hsv.b,0,1);
//将累计后的hsv 转换成rgb然后输出颜色
float3 resultColor = hsv2rgb(thsv);
return float4(resultColor,sColor.a);
}
//不符合条件的像素,返回输入的颜色即可
return sColor;
}
颜色替换hlsl1).在代码里定义了三个属性
1.sourceColor : 源颜色,用于匹配输入的颜色像素;
2.hsv : 输入的hsv 颜色,用于跟符合条件的像素进行累加计算。(在这里我将hsv的三个分量的范围都设置成了-1~1之间了,因为使用到的两个转换函数返回的值都是在0~1之间的。)
3.threshold: 阈值范围;
2).在代码中定义了两个 颜色模式转换的函数,因为这两个函数是我从别的地方复制过来的所以我也不是很懂。
颜色转换引用:Unity Shader - HSV 和 RGB 的相互转换 - 知乎 (zhihu.com)
二.选区效果
从效果图中可以看到我在图像的右下角显示了一个显示当前选中像素的效果图,其实这个也是用hlsl 绘制出来了,在这里也将这个hlsl 贴出来,因为代码上已经写了注释了,在这里就不详细讲了;
1 //声明只有一张纹理输入
2 #define D2D_INPUT_COUNT 1
3 //将第一张纹理设置为简单模式采样
4 #define D2D_INPUT0_SIMPLE
5 //引入hlsl 帮助程序库
6 #include "d2d1effecthelpers.hlsli"
7
8 //输入颜色,用户配备
9 float3 color;
10 //阈值
11 float threshold;
12
13 // HSV 到 RGB 转换
14 float3 hsv2rgb(float3 c)
15 {
16 float3 rgb = clamp(abs(fmod(c.x * 6.0 + float3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
17 float3 f1 = float3(1, 1, 1);
18 return c.z * lerp(f1, rgb, float3(c.y, c.y, c.y));
19 }
20
21 D2D_PS_ENTRY(main){
22 //获取当前像素颜色
23 float4 sColor = D2DGetInput(0);
24 //将当前像素的rgb每个分量减去输入颜色的每个分量
25 //用于跟阈值对比,查看当前像素是否符合条件
26 float r = abs(sColor.r - color.r);
27 float g = abs(sColor.g - color.g);
28 float b = abs(sColor.b - color.b);
29 if( r< threshold && g < threshold && b < threshold)
30 {
31 //将色相和饱和度都设置为0
32 //将rgb三个分量相加后除以阈值*3 计算出比率然后设置当前像素的亮度
33 float3 hsv = float3(0, 0, 0);
34 hsv.b= 1-(r+g+b)/(threshold*3);
35 //用hsv2rgb 将hsv 转换成rgb颜色
36 float3 tColor = hsv2rgb(hsv);
37 return float4(tColor,sColor.a);
38 }
39 //不符合条件的像素返回黑色
40 return float4(0,0,0,1);
41 }
选区hlsl三.绘制方法,传入参数设定
1 canvas.Draw += (s, e) =>
2 {
3 //绘制黑白网格
4 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100);
5 //判断effect 和位图是否为空
6 if (effect == null || bitmap == null)
7 return;
8 if (color != null)
9 {
10 //绘制替换颜色效果
11 //颜色将0~255 转换成0~1 因为hlsl里面的颜色是0~1范围的并且是float类型,所以这里需要将每个颜色的分量除以255
12 Vector3 hsv = targetHsv.HsvToVector3();
13 Vector3 sourceColor = new Vector3(color.Value.R / 255f, color.Value.G / 255f, color.Value.B / 255f);
14 //通过键值对的形式设置属性传递到着色器里面
15 effect.Properties["threshold"] = (float)threshold.Value;
16 effect.Properties["hsv"] = hsv;
17 effect.Properties["sourceColor"] = sourceColor;
18 effect.Source1 = bitmap;
19 //绘制效果图
20 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
21 effectTran.Source = effect;
22 e.DrawingSession.DrawImage(effectTran);
23 //绘制选区图 右下角
24 var constTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
25 constEffect.Source1 = bitmap;
26 constEffect.Properties["threshold"] = (float)threshold.Value;
27 constEffect.Properties["color"] = sourceColor;
28 using (var ds = constRender.CreateDrawingSession())
29 {
30 ds.Clear(Colors.Transparent);
31 ds.DrawImage(constEffect, new Rect(new Point(), bitmap.Size), new Rect(new Point(), bitmap.Size));
32 }
33 constTran.Source = constRender;
34 e.DrawingSession.DrawImage(constTran, previewWidth, effectHeight);
35 }
36 else
37 {
38 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
39 effectTran.Source = bitmap;
40 e.DrawingSession.DrawImage(effectTran);
41 }
42 //绘制原图 左下角
43 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
44 previewTran.Source = bitmap;
45 e.DrawingSession.DrawImage(previewTran, 0, effectHeight);
46 //绘制选择颜色缩略图
47 if (isPressed && (pos.X >= offsetX && pos.X <= offsetX + width && pos.Y >= offsetY && pos.Y <= offsetY + height))
48 {
49 //画三角形
50 CanvasPathBuilder builder = new CanvasPathBuilder(canvas);
51 builder.BeginFigure(pos);
52 builder.AddLine(pos - new Vector2(5, 5));
53 builder.AddLine(pos - new Vector2(-5, 5));
54 builder.EndFigure(CanvasFigureLoop.Closed);
55 CanvasGeometry geometry = CanvasGeometry.CreatePath(builder);
56 e.DrawingSession.FillGeometry(geometry, Colors.White);
57 e.DrawingSession.FillRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.White);
58 //采样位置
59 var usePx = (pos.X - offsetX) / scale;
60 var usePy = (pos.Y - offsetY) / scale;
61 e.DrawingSession.DrawImage(bitmap, pos.X - 50, pos.Y - 100 - 5, new Rect(usePx - 50, usePy - 50, 100, 100));
62 e.DrawingSession.DrawRectangle(new Rect(pos.X - 1, pos.Y - 56, 3, 3), Colors.Orange);
63 e.DrawingSession.DrawRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.Black);
64 }
65 };
Draw在绘制方法里面我们只需要关心如和传入参数和参数类型以及范围
1.sourceColor: 通过键值对的形式设置效果的参数,但是在传入颜色之前需要 从rgb(0~255) 转换成 vector3(0~1);
2.hsv :在传入前需要将hsv颜色 从 (360,100,100)转换成 (1,1,1)
3.threshold: 因为在hlsl 上定义的是float 类型,所以传入时 也需要将值转换成float类型才能传入;
四.所有代码
因为下面的很多代码都是关于一些输入交互的所以这里直接将代码贴出来,复制到已经引用win2d库的项目应该就能运行了。
1 <Grid>
2 <Grid.RowDefinitions>
3 <RowDefinition></RowDefinition>
4 <RowDefinition Height="auto"></RowDefinition>
5 </Grid.RowDefinitions>
6 <canvas:CanvasControl x:Name="canvas"></canvas:CanvasControl>
7 <StackPanel Grid.Row="1">
8 <Button Content="选择图片" x:Name="selectPicture"></Button>
9 <Slider Header="阈值:" Value="0.2" Maximum="1" StepFrequency="0.01" x:Name="threshold"></Slider>
10 <StackPanel Orientation="Horizontal">
11 <Border Margin="10 0 10 0" x:Name="selectColor" BorderBrush="Black" BorderThickness="2">
12 <TextBlock Text="选择的颜色" HorizontalAlignment="Center" VerticalAlignment="Center" ></TextBlock>
13 </Border>
14 <StackPanel Width="300">
15 <Rectangle Width="200" Fill="Gray" Height="30" x:Name="replaceColor"></Rectangle>
16 <Slider Header="色相" x:Name="hue" StepFrequency="1" Minimum="-180" Maximum="180"></Slider>
17 <Slider Header="饱和度" x:Name="saturation" Minimum="-100" Maximum="100"></Slider>
18 <Slider Header="亮度" x:Name="brightness" Minimum="-100" Maximum="100"></Slider>
19 </StackPanel>
20 </StackPanel>
21
22 </StackPanel>
23 </Grid>
Xaml
1 public sealed partial class ColorReplacementPage : Page
2 {
3 PixelShaderEffect effect;
4 //选区效果
5 PixelShaderEffect constEffect;
6 CanvasRenderTarget constRender;
7 CanvasBitmap bitmap;
8 Color[] bitmapColors;
9 public ColorReplacementPage()
10 {
11 this.InitializeComponent();
12 Init();
13 InitOperate();
14 }
15 Hsv targetHsv = new Hsv();
16 Hsv sourceHsv = new Hsv();
17 Color? color;
18 float effectWidth;
19 float effectHeight;
20 float previewWidth;
21 float previewHeight;
22 //记录鼠标是否在画布上按下
23 bool isPressed;
24 //记录小图在画布上的大小和位置,和偏移
25 float offsetX, offsetY, scale, width, height;
26 Vector2 pos;
27 void Init()
28 {
29 canvas.CreateResources += async (s, e) =>
30 {
31 //获取着色器二进制文件
32 StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ColorReplacementEffect.bin"));
33 IBuffer buffer = await FileIO.ReadBufferAsync(file);
34 //转换成字节数组
35 var bytes = buffer.ToArray();
36 //用 字节数组 初始化一个 PixelShaderEffect 对象;b v
37 effect = new PixelShaderEffect(bytes);
38
39 file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ConstituencyEffect.bin"));
40 buffer = await FileIO.ReadBufferAsync(file);
41 bytes = buffer.ToArray();
42 constEffect = new PixelShaderEffect(bytes);
43 };
44 //选择图片
45 selectPicture.Click += async (s, e) =>
46 {
47 var file = await Ulit.SelectFileAsync(new List<string> { ".png", ".jpg" });
48 if (file == null)
49 {
50 bitmap = null;
51 color = null;
52 }
53 else
54 {
55 bitmap = await CanvasBitmap.LoadAsync(canvas.Device, await file.OpenAsync(FileAccessMode.Read));
56 //获取图像像素数组
57 bitmapColors = bitmap.GetPixelColors();
58 constRender = new CanvasRenderTarget(canvas, bitmap.Size);
59 }
60 CalcuationDrawPosition();
61 canvas.Invalidate();
62 };
63 canvas.Draw += (s, e) =>
64 {
65 //绘制黑白网格
66 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100);
67 //判断effect 和位图是否为空
68 if (effect == null || bitmap == null)
69 return;
70 if (color != null)
71 {
72 //绘制替换颜色效果
73 //颜色将0~255 转换成0~1 因为hlsl里面的颜色是0~1范围的并且是float类型,所以这里需要将每个颜色的分量除以255
74 Vector3 hsv = targetHsv.HsvToVector3();
75 Vector3 sourceColor = new Vector3(color.Value.R / 255f, color.Value.G / 255f, color.Value.B / 255f);
76 //通过键值对的形式设置属性传递到着色器里面
77 effect.Properties["threshold"] = (float)threshold.Value;
78 effect.Properties["hsv"] = hsv;
79 effect.Properties["sourceColor"] = sourceColor;
80 effect.Source1 = bitmap;
81 //绘制效果图
82 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
83 effectTran.Source = effect;
84 e.DrawingSession.DrawImage(effectTran);
85 //绘制选区图 右下角
86 var constTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
87 constEffect.Source1 = bitmap;
88 constEffect.Properties["threshold"] = (float)threshold.Value;
89 constEffect.Properties["color"] = sourceColor;
90 using (var ds = constRender.CreateDrawingSession())
91 {
92 ds.Clear(Colors.Transparent);
93 ds.DrawImage(constEffect, new Rect(new Point(), bitmap.Size), new Rect(new Point(), bitmap.Size));
94 }
95 constTran.Source = constRender;
96 e.DrawingSession.DrawImage(constTran, previewWidth, effectHeight);
97 }
98 else
99 {
100 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
101 effectTran.Source = bitmap;
102 e.DrawingSession.DrawImage(effectTran);
103 }
104 //绘制原图 左下角
105 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
106 previewTran.Source = bitmap;
107 e.DrawingSession.DrawImage(previewTran, 0, effectHeight);
108 //绘制选择颜色缩略图
109 if (isPressed && (pos.X >= offsetX && pos.X <= offsetX + width && pos.Y >= offsetY && pos.Y <= offsetY + height))
110 {
111 //画三角形
112 CanvasPathBuilder builder = new CanvasPathBuilder(canvas);
113 builder.BeginFigure(pos);
114 builder.AddLine(pos - new Vector2(5, 5));
115 builder.AddLine(pos - new Vector2(-5, 5));
116 builder.EndFigure(CanvasFigureLoop.Closed);
117 CanvasGeometry geometry = CanvasGeometry.CreatePath(builder);
118 e.DrawingSession.FillGeometry(geometry, Colors.White);
119 e.DrawingSession.FillRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.White);
120 //采样位置
121 var usePx = (pos.X - offsetX) / scale;
122 var usePy = (pos.Y - offsetY) / scale;
123 e.DrawingSession.DrawImage(bitmap, pos.X - 50, pos.Y - 100 - 5, new Rect(usePx - 50, usePy - 50, 100, 100));
124 e.DrawingSession.DrawRectangle(new Rect(pos.X - 1, pos.Y - 56, 3, 3), Colors.Orange);
125 e.DrawingSession.DrawRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.Black);
126 }
127 };
128 threshold.ValueChanged += (s, e) => canvas.Invalidate();
129 RangeBaseValueChangedEventHandler hsvChanged = (s, e) =>
130 {
131 if (color == null)
132 return;
133
134 if (s is Slider slider)
135 {
136 switch (slider.Name)
137 {
138 case "hue":
139 targetHsv.H = e.NewValue;
140 break;
141 case "saturation":
142 targetHsv.S = e.NewValue;
143 break;
144 case "brightness":
145 targetHsv.V = e.NewValue;
146 break;
147 }
148 replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb());
149 }
150 canvas.Invalidate();
151 };
152 hue.ValueChanged += hsvChanged;
153 saturation.ValueChanged += hsvChanged;
154 brightness.ValueChanged += hsvChanged;
155 }
156
157 void InitOperate()
158 {
159 //画布大小更改,计算绘制区域
160 canvas.SizeChanged += (s, e) => CalcuationDrawPosition();
161 //鼠标再画布按下
162 canvas.PointerPressed += (s, e) =>
163 {
164 isPressed = true;
165 canvas.Invalidate();
166 };
167 //鼠标松开
168 canvas.PointerReleased += (s, e) =>
169 {
170 isPressed = false;
171 canvas.Invalidate();
172 };
173
174 canvas.PointerMoved += (s, e) =>
175 {
176 if (!isPressed || bitmap == null)
177 return;
178 Point p = e.GetCurrentPoint(canvas).Position;
179 pos = p.ToVector2();
180 //是否进入范围
181 if ((p.X >= offsetX && p.X <= offsetX + width && p.Y >= offsetY && p.Y <= offsetY + height))
182 {
183 var x = p.X - offsetX;
184 var y = p.Y - offsetY;
185 x = x / scale;
186 y = y / scale;
187 color = bitmapColors[(int)x + (int)y * bitmap.SizeInPixels.Width];
188 sourceHsv = sourceHsv.RgbToHsv(color.Value);
189 replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb());
190 //设置选中的颜色
191 selectColor.Background = new SolidColorBrush(color.Value);
192 canvas.Invalidate();
193 }
194 };
195 }
196 void CalcuationDrawPosition()
197 {
198 //判断effect 和位图是否为空
199 if (effect == null || bitmap == null)
200 return;
201 var element = canvas;
202 effectWidth = (float)element.ActualWidth;
203 effectHeight = (float)element.ActualHeight * 0.7f;
204 previewWidth = effectWidth*0.5f;
205 previewHeight = (float)element.ActualHeight * 0.3f;
206 var mat = Win2dUlit.CalcutateImageCenteredMat(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
207 scale = mat.M11;
208 offsetX = mat.M31;
209 offsetY = mat.M32 + effectHeight;
210 width = (float)(scale * bitmap.Size.Width);
211 height = (float)(scale * bitmap.Size.Height);
212 }
213 }
后台代码
1 public static class Win2dUlit
2 {
3 public static void DrawGridGraphics(CanvasDrawingSession draw, int size = 100)
4 {
5 var target = new CanvasRenderTarget(draw, new Windows.Foundation.Size(size * 2, size * 2));
6 using (var ds = target.CreateDrawingSession())
7 {
8 for (int x = 0; x < 2; x++)
9 {
10 for (int y = 0; y < 2; y++)
11 {
12 Color color = (x + y) % 2 == 0 ? Colors.White : Color.FromArgb(255, 219, 219, 219);
13 ds.FillRectangle(new Rect(x * size, y * size, size, size), color);
14 }
15 }
16 }
17 var gridTargetEffect = new BorderEffect()
18 {
19 Source = target,
20 ExtendY = CanvasEdgeBehavior.Wrap,
21 ExtendX = CanvasEdgeBehavior.Wrap,
22 };
23 draw.DrawImage(gridTargetEffect);
24 }
25
26 public static Transform2DEffect CalcutateImageCenteredTransform(double cWidth, double cHeight, double iWidth, double iHeight)
27 {
28 var mat = CalcutateImageCenteredMat(cWidth, cHeight, iWidth, iHeight);
29 return new Transform2DEffect() { TransformMatrix = mat };
30 }
31
32 public static Transform2DEffect CalcutateImageCenteredTransform(Windows.Foundation.Size cSize, Windows.Foundation.Size iSize)
33 {
34 return CalcutateImageCenteredTransform(cSize.Width, cSize.Height, iSize.Width, iSize.Height);
35 }
36
37 public static Matrix3x2 CalcutateImageCenteredMat(Windows.Foundation.Size cSize, Windows.Foundation.Size iSize)
38 {
39 return CalcutateImageCenteredMat(cSize.Width, cSize.Height, iSize.Width, iSize.Height);
40 }
41 public static Matrix3x2 CalcutateImageCenteredMat(double cWidth, double cHeight, double iWidth, double iHeight)
42 {
43 float f = (float)Math.Min(cWidth / iWidth, cHeight / iHeight);
44 float ox = (float)(cWidth - iWidth * f) / 2;
45 float oy = (float)(cHeight - iHeight * f) / 2;
46 Matrix3x2 matrix3X2 = Matrix3x2.CreateScale(f) * Matrix3x2.CreateTranslation(ox, oy);
47 return matrix3X2;
48 }
49 }
Win2dUlit
1 public class Hsv
2 {
3 public Hsv() { }
4 public Hsv(double h, double s, double v)
5 {
6 H = h;
7 S = s;
8 V = v;
9 }
10 /// <summary>
11 /// 色相 0~360
12 /// </summary>
13 public double H { get;set; }
14 /// <summary>
15 /// 亮度 0~100
16 /// </summary>
17 public double S { get;set; }
18 /// <summary>
19 /// 饱和度 0~100
20 /// </summary>
21 public double V { get;set; }
22
23 /// <summary>
24 /// 将当前hsv转换成颜色返回
25 /// </summary>
26 public Color HsvToRgb()
27 {
28 double s = this.S / 100;
29 double v = this.V / 100;
30 double h = this.H;
31 double R = 0, G = 0, B = 0;
32 if (s == 0)
33 R = G = B = v;
34 else
35 h /= 60;
36 int i = (int)(h);
37 double f = h - i;
38 double a = v * (1 - s);
39 double b = v * (1 - s * f);
40 double c = v * (1 - s * (1 - f));
41 switch (i)
42 {
43 case 0:
44 case 6:
45 R = v; G = c; B = a;
46 break;
47 case 1:
48 R = b; G = v; B = a;
49 break;
50 case 2:
51 R = a; G = v; B = c;
52 break;
53 case 3:
54 R = a; G = b; B = v;
55 break;
56 case 4:
57 R = c; G = a; B = v;
58 break;
59 case 5:
60 R = v; G = a; B = b;
61 break;
62 }
63 R *= 255;
64 G *= 255;
65 B *= 255;
66 Color color = Color.FromArgb(255, (byte)R, (byte)G, (byte)B);
67 return color;
68 }
69 public Hsv RgbToHsv(Color c)
70 {
71 int R, G, B;
72 R = c.R;
73 G = c.G;
74 B = c.B;
75 double V, S, H = 0;
76 double max = Math.Max(R, Math.Max(G, B));
77 double min = Math.Min(R, Math.Min(G, B));
78 var dalte = max - min;
79 V = max / 255;
80 S = dalte == 0 ? 0 : dalte / max;
81 if (R == max) H = (G - B) / (max - min) * 60;
82 if (G == max) H = 120 + (B - R) / (max - min) * 60;
83 if (B == max) H = 240 + (R - G) / (max - min) * 60;
84 if (dalte == 0) H = 0;
85 if (H < 0)
86 H = H + 360;
87 Hsv hsv = new Hsv() { H = (int)H, S = S * 100, V = V * 100 };
88 return hsv;
89 }
90 public static Hsv operator +(Hsv a, Hsv b)
91 {
92 var h = (a.H + b.H) % 360;
93 var s = (a.S + b.S) % 100;
94 var v = (a.V + b.V) % 100;
95 return new Hsv(h, s, v);
96 }
97 public Vector3 HsvToVector3()
98 {
99 var x = (float)(this.H / 360.0);
100 var y = (float)(this.S / 100.0);
101 var z = (float)(this.V / 100.0);
102 return new Vector3(x, y, z);
103 }
104 }
105 }
Hsv
五.总结
通过使用hlsl着色器代码,我们就可以实现自定义滤镜,来实现不同的效果。
下载微PE工具箱进入官网下载微PE工具箱-下载 安装好后,打开微PE工具箱客户端,选择安装PE到U盘 PE壁纸可选择自己喜欢的壁纸,勾选上包含DOS工具箱,个性化盘符图标 下载原版系统进入网站下载镜像NEXT,ITELLYOU如果没有账号,注册一下就好进入选择开始使用选择win10 这里我们选择消费者版,用迅雷把BT种子下载下来 下面的两个盘符,是PE工具箱安装进U盘后,分成的盘符,注意EFI的盘符,这里面不能删东西,也不能添东西,另一个盘符可以当做正常的U盘空间使用,我们现在需要把下载下来的景象文件复制到正常的U盘空间中去 这个时候我们的系统U盘就只做好了 安装系统我们将U盘插入电脑,开机,
跳过联网激活:OOBE界面直接按Ctrl+Shift+F3进入审核模式。这样就可以直接进入系统进行一些硬件测试等,而不用联网激活导致新机无法退货。需要注意的是,在审核模式下进行的一些操作都会保留,并不会在退出后自动还原!安装的软件在正常开机进系统后还会看见!如果电脑确实没连互联网又不想强行跳过OOBE(网上很多教程会叫你直接结束OOBE进程,但这是不推荐的,因为一些厂商自带优化程序和系统初始化设置在后面都会应用,对于笔记本跳过的话你会发现驱动和内置应用都没有装上。其实这部分脚本就在系统盘的Recovery隐藏文件夹下),可以参考以下方式:https://www.landiannews.com/
我正在尝试在我的机器上安装win32-apigem,但在构建native扩展时我遇到了一些问题:$geminstallwin32-api--no-ri--rdocTemporarilyenhancingPATHtoincludeDevKit...Buildingnativeextensions.Thiscouldtakeawhile...C:\Programs\dev_kit\bin\make.exe:***Couldn'treservespaceforcygwin'sheap,Win32error0ERROR:Errorinstallingwin32-api:ERROR:Failed
我在Windows上运行ruby1.9.2并试图移植在Ruby1.8中工作的代码。该代码使用以前运行良好的Open4.popen4。对于1.9.2,我做了以下事情:通过geminstallPOpen4安装了POpen4需要POpen4通过require'popen4'尝试像这样使用POpen4:Open4.popen4("cmd"){|io_in,io_out,io_er|...}当我这样做时,我得到了错误:nosuchfiletoload--win32/open3如果我尝试安装win32-open3(geminstallwin32-open3),我会收到错误消息:win32-op
使用PythonWin32COM如何获取对图表数据表的引用?我可以使用数据表创建图表(PowerPoint将其弹出在单独的窗口中),例如:importwin32comfromMSOimportconstantsasmsoconstApplication=win32com.client.Dispatch("PowerPoint.Application")Application.Visible=TruePresentation=Application.Presentations.Add()FirstSlide=Presentation.Slides.Add(1,12)...noproblemadd
我正在为RaspberryPi开发其UWP应用程序的应用程序,因此我想从UWP应用程序发送和接收字符到Android应用程序。因此,请告诉我什么是UWP应用程序的代码。提前致谢:)看答案此站点向您展示如何发送和接收串行蓝牙数据。github上的来源
一、我的情况:win10系统下Edge浏览器搜索引擎[必应]和新建标签页被篡改百度的搜索引擎和百度的页面我的解决方案步骤如下:1.检查电脑管家的浏览器保护检查一下你的电脑是否后台开启了电脑管家;如果是,则检查一下是否开启了浏览器保护。由于我是联想的笔记本,自带联想管家,一直没有关闭过它,以我的电脑为例,进行关闭,如下图所示。其他的电脑管家软件,应该也有类似的功能,耐心找一下就能找到啦。2.在Edge浏览中进行相关设置先点击浏览器右上角的三个点,然后找到“设置”,然后进入设置界面。在当前页面手动搜索“搜索引擎”,然后选择需要的搜索引擎“必应”,然后点击管理搜索引擎。进入管理搜索引擎界面后,理论上
【修电脑】VMware从GHO文件备份恢复Win10/Win7系统注意参考硬盘知识一、硬盘接口的分类二、硬盘的分类按照硬盘材质分为两大类按照接口类型区分boot启动知识LegacyBIOS引导uefi引导启动流程查看系统的引导启动方式1.VMware新建win10x64系统2.制作老毛桃U盘winpe3.VMwarewin10从winpe老毛桃U盘启动4.GHO文件还原系统(失败,勿复现)5.GHO转vmdk6.成功实现经验总结注意本文仅供参考学习,任何因阅读者操作导致的数据损失和破坏,本文作者概不负责!参考GHOST文件如何导入虚拟机硬盘知识一、硬盘接口的分类硬盘接口通常分为五种类型:SAT
文章目录方法1,通过组策略禁用(未验证)1、首先按下键盘“Win+R”打开运行。2、接着在其中输入“gpedit.msc”回车打开组策略。3、然后进入计算机配置下“用户配置”中的“管理模板”4、再打开“系统”下的“驱动程序安装”5、进入后,双击打开其中的“设备驱动程序的代码签名”6、最后勾选“已启用”并将选项改成“忽略”再确定保存即可禁用强制签名。方法2(临时禁用)1、打开并登录操作系统左下角。开始菜单上单击选择设置。2、在Windows设置页面选择更新和安全。3、在更新和安全页面选择左侧的恢复标签,在右侧选择立即重新启动。4、在新的启动页面选择疑难解答。5、在疑难解答页面选择高级选项。6、在
是否可以在浏览器IE、Chrome、Firefox、Opera中使用Javascript获取Windows用户名和PCName? 最佳答案 没有。此类信息不会暴露给浏览器中的javascript引擎。 关于javascript-获取OSWin7用户名Javascript,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8802602/