Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 913c3d8

Browse filesBrowse files
committed
Webcam reflection prototype.
1 parent 91eec91 commit 913c3d8
Copy full SHA for 913c3d8

File tree

2 files changed

+97
-7
lines changed
Filter options

2 files changed

+97
-7
lines changed

‎CrtTextBox/CrtTextBox.csproj

Copy file name to clipboardExpand all lines: CrtTextBox/CrtTextBox.csproj
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" />
2121

2222
<AvaloniaResource Include="Assets\**"/>
23+
24+
<PackageReference Include="OpenCvSharp4" Version="4.10.0.20240616" />
25+
26+
<PackageReference Include="OpenCvSharp4.runtime.osx.10.15-universal" Version="4.7.0.20230224" />
2327
</ItemGroup>
2428

2529

‎CrtTextBox/ShaderControl.cs

Copy file name to clipboardExpand all lines: CrtTextBox/ShaderControl.cs
+93-7Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
using Avalonia.Rendering.Composition;
2121
using Avalonia.Skia;
2222
using Avalonia.Threading;
23+
using OpenCvSharp;
2324
using SkiaSharp;
25+
using Rect = Avalonia.Rect;
26+
using Size = Avalonia.Size;
2427

2528
namespace RenderTest;
2629

@@ -46,12 +49,15 @@ public class ShaderControl : UserControl
4649
/// </summary>
4750
public static readonly StyledProperty<int> FpsProperty = AvaloniaProperty.Register<ShaderControl, int>(nameof(Fps), 30);
4851

52+
private VideoCapture m_videoCapture;
53+
private Mat m_webcamFrame;
54+
4955
static ShaderControl()
5056
{
5157
AffectsRender<ShaderControl>(ShaderUriProperty);
5258
AffectsMeasure<ShaderControl>(ShaderUriProperty);
5359
}
54-
60+
5561
/// <summary>
5662
/// Gets or sets the frames per second (FPS) at which the source control is sampled.
5763
/// </summary>
@@ -69,7 +75,7 @@ public Uri ShaderUri
6975
get => GetValue(ShaderUriProperty);
7076
set => SetValue(ShaderUriProperty, value);
7177
}
72-
78+
7379
/// <summary>
7480
/// Gets or sets the control source to which the shader effect will be applied.
7581
/// </summary>
@@ -105,7 +111,7 @@ public Control ControlSource
105111
/// </remarks>
106112
public void AddUniform(string name, bool value) =>
107113
AddUniform(name, value ? 1.0f : 0.0f);
108-
114+
109115
/// <summary>
110116
/// Call to set a constant uniform float/float2/float3/float4 value, to be passed to the shader code each frame.
111117
/// </summary>
@@ -146,14 +152,88 @@ private void RenderSourceAsImage()
146152
using var rtb = new RenderTargetBitmap(new PixelSize(m_sourceControlBitmap.Width, m_sourceControlBitmap.Height));
147153
rtb.Render(ControlSource);
148154

155+
// Copy the rendered pixels to the bitmap.
149156
rtb.CopyPixels(
150157
new PixelRect(0, 0, m_sourceControlBitmap.Width, m_sourceControlBitmap.Height),
151158
m_sourceControlBitmap.GetPixels(),
152159
m_sourceControlBitmap.ByteCount,
153160
m_sourceControlBitmap.RowBytes);
154161

162+
ApplyWebcamOverlay();
163+
155164
m_visualHandler.SourceBitmap = m_sourceControlBitmap;
156165
}
166+
private unsafe void ApplyWebcamOverlay()
167+
{
168+
if (Design.IsDesignMode)
169+
return;
170+
171+
if (m_videoCapture == null)
172+
{
173+
m_videoCapture = new VideoCapture(0);
174+
175+
// Set the desired resolution (e.g., 640x480)
176+
m_videoCapture.Set(VideoCaptureProperties.FrameWidth, 640);
177+
m_videoCapture.Set(VideoCaptureProperties.FrameHeight, 480);
178+
m_webcamFrame ??= new Mat();
179+
}
180+
181+
m_videoCapture.Read(m_webcamFrame);
182+
var resizedFrame = m_webcamFrame;
183+
var resized = false;
184+
185+
// Check if resizing is necessary
186+
if (m_webcamFrame.Width != m_sourceControlBitmap.Width || m_webcamFrame.Height != m_sourceControlBitmap.Height)
187+
{
188+
resizedFrame = m_webcamFrame.Resize(new OpenCvSharp.Size(m_sourceControlBitmap.Width, m_sourceControlBitmap.Height));
189+
resized = true;
190+
}
191+
192+
var pixels = m_sourceControlBitmap.GetPixels(); // Get pixel data pointer
193+
var pixelSpan = new Span<byte>((void*)pixels, m_sourceControlBitmap.ByteCount);
194+
for (var y = 0; y < m_sourceControlBitmap.Height; y++)
195+
{
196+
for (var x = 0; x < m_sourceControlBitmap.Width; x++)
197+
{
198+
// Ranges from 0.0 (no desaturation) to 1.0 (full desaturation).
199+
var desaturationAmount = 0.8;
200+
201+
// Get the pixel index
202+
var pixelIndex = (y * m_sourceControlBitmap.Width + x) * 4;
203+
204+
// Read the original color
205+
var origR = pixelSpan[pixelIndex];
206+
var origG = pixelSpan[pixelIndex + 1];
207+
var origB = pixelSpan[pixelIndex + 2];
208+
209+
// Read the webcam pixel data (RGB)
210+
var camColor = resizedFrame.At<Vec3b>(y, m_sourceControlBitmap.Width - x - 1);
211+
212+
// Convert webcam color to grayscale (luminance)
213+
var camLuminance = 0.21 * camColor.Item2 + 0.72 * camColor.Item1 + 0.07 * camColor.Item0;
214+
215+
// Desaturate webcam color by blending the grayscale value with the original webcam color
216+
var camR = (camColor.Item2 * (1.0 - desaturationAmount)) + (camLuminance * desaturationAmount);
217+
var camG = (camColor.Item1 * (1.0 - desaturationAmount)) + (camLuminance * desaturationAmount);
218+
var camB = (camColor.Item0 * (1.0 - desaturationAmount)) + (camLuminance * desaturationAmount);
219+
220+
// Blend the desaturated webcam color with the original image color
221+
var reflectionIntensity = 0.025; // Adjust this value to control reflection strength (0.0 to 1.0)
222+
var r = (byte)Math.Min(255, origR * (1 - reflectionIntensity) + camR * reflectionIntensity);
223+
var g = (byte)Math.Min(255, origG * (1 - reflectionIntensity) + camG * reflectionIntensity);
224+
var b = (byte)Math.Min(255, origB * (1 - reflectionIntensity) + camB * reflectionIntensity);
225+
226+
// Set new color in the span
227+
pixelSpan[pixelIndex] = r;
228+
pixelSpan[pixelIndex + 1] = g;
229+
pixelSpan[pixelIndex + 2] = b;
230+
}
231+
}
232+
233+
// Dispose of the resized frame if it was created.
234+
if (resized)
235+
resizedFrame.Dispose();
236+
}
157237

158238
private Size GetShaderSize() => m_controlSource?.Bounds.Size ?? new Size(512, 512);
159239

@@ -162,8 +242,7 @@ protected override Size MeasureOverride(Size availableSize) =>
162242

163243
protected override Size ArrangeOverride(Size finalSize)
164244
{
165-
var source = ShaderUri;
166-
if (source == null)
245+
if (ShaderUri == null)
167246
return new Size();
168247

169248
var sourceSize = GetShaderSize();
@@ -237,8 +316,12 @@ private void Start()
237316
private void Stop() =>
238317
m_customVisual?.SendHandlerMessage(new ShaderVisualHandler.DrawPayload(ShaderVisualHandler.Command.Stop));
239318

240-
private void DisposeImpl() =>
319+
private void DisposeImpl()
320+
{
241321
m_customVisual?.SendHandlerMessage(new ShaderVisualHandler.DrawPayload(ShaderVisualHandler.Command.Dispose));
322+
m_webcamFrame?.Dispose();
323+
m_videoCapture?.Dispose();
324+
}
242325

243326
/// <summary>
244327
/// Apply new shader code.
@@ -269,7 +352,10 @@ private class ShaderVisualHandler : CompositionCustomVisualHandler
269352
public ShaderVisualHandler(Dictionary<string, Func<float[]>> uniforms)
270353
{
271354
m_customUniforms = uniforms;
272-
m_customUniforms["iTime"] = () => new[] { (float)CompositionNow.TotalSeconds };
355+
m_customUniforms["iTime"] = () => new[]
356+
{
357+
(float)CompositionNow.TotalSeconds
358+
};
273359
}
274360

275361
/// <summary>

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.