Skip to content

Instantly share code, notes, and snippets.

@AldeRoberge
Created April 18, 2025 02:46
Show Gist options
  • Save AldeRoberge/51393499d1e66d6282c21c5f9f959a6d to your computer and use it in GitHub Desktop.
Save AldeRoberge/51393499d1e66d6282c21c5f9f959a6d to your computer and use it in GitHub Desktop.
Captures an image from the webcam and quickly analyzes whether a person is present.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.AI;
using OpenCvSharp;
using AForge.Video.DirectShow;
namespace ADG.Playground.Vision
{
internal class VisionResponse
{
public bool IsPersonPresent { get; set; }
public float Confidence { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
// Build host with DI
var builder = Host.CreateApplicationBuilder(args);
// Register Ollama chat client
builder.Services.AddChatClient(
new OllamaChatClient(
new Uri("http://localhost:11434"),
modelId: "gemma3:12b"
)
);
var host = builder.Build();
var chatClient = host.Services.GetRequiredService<IChatClient>();
while (true)
{
string imagePath;
try
{
imagePath = await CaptureImageAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Error capturing image: {ex.Message}");
break;
}
if (string.IsNullOrEmpty(imagePath))
{
Console.WriteLine("No image captured.");
break;
}
// Read image and prepare chat message
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
var chatMessage = new ChatMessage(ChatRole.User, "Analyze this image for person presence.")
{
Contents = { new DataContent(imageBytes, "image/png") }
};
// Send to AI service and display result
var response = await chatClient.GetResponseAsync<VisionResponse>(chatMessage);
Console.WriteLine(
$"Person present: {response.Result.IsPersonPresent}, " +
$"confidence: {response.Result.Confidence:F2}"
);
}
}
private static async Task<string> CaptureImageAsync()
{
const string assetsFolder = "Assets";
const string fileName = "captured";
string currentDateTimeAsFileName = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string filePath = Path.Combine(assetsFolder, $"{fileName}_{currentDateTimeAsFileName}.png");
Directory.CreateDirectory(assetsFolder);
// List available video devices
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices.Count == 0)
throw new InvalidOperationException("No video devices found.");
Console.WriteLine("Available video devices:");
for (int i = 0; i < videoDevices.Count; i++)
{
Console.WriteLine($"[{i}] {videoDevices[i].Name}");
}
const int deviceIndex = 7;
if (deviceIndex < 0 || deviceIndex >= videoDevices.Count)
throw new ArgumentOutOfRangeException(nameof(deviceIndex), "Invalid device index.");
Console.WriteLine($"Using device: {videoDevices[deviceIndex].Name}");
using var capture = new VideoCapture(deviceIndex); // or use CAP_ANY
if (!capture.IsOpened())
throw new InvalidOperationException("Webcam could not be opened.");
// Warm up
using var temp = new Mat();
for (int i = 0; i < 10; i++)
{
capture.Read(temp);
await Task.Delay(100); // wait a bit between frames
}
using var frame = new Mat();
capture.Read(frame);
if (frame.Empty())
throw new InvalidOperationException("Captured frame is empty (black).");
Cv2.ImWrite(filePath, frame);
return Path.GetFullPath(filePath);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment