Herkese hayırlı akşamlar. Bugün sizlere sevgili eski yöneticilerimizden @ARWENDA 'nın profilim üzerinde yapmış olduğu bir paylaşımdan esinlenerek uzun süredir araştırmakta olduğum bir projeyi bugün tamamlamış bulunmaktayım. Konu aslında biraz amacından sapmış olsa da en azından Python üzerinde gerçekleşen OpenCV, MediaPipe kütüphaneleri sayesinde hareketleri algılayıp parmakları tespit edip mmap ( Python ↔ C# / C++ / başka bir süreç arası veri paylaşımı kütüphanesi ) sayesinde C# formumuza verimizi aktaracağız bu verileri aktarırken de senkronize bir şekilde gönderilmesi için win32event kullanacağız. İlk başlarda birkaç yöntem denedim ancak pahalı ekipmanlar & araştırmalarım esnasında çıkan bazı aksaklıklar nedeniyle sanırım yapamayacağım demiştim ki aklıma geçmişte paylaşmış olduğum yüzü algılayan OpenCV kütüphanesi aklıma geldi ancak ağırlık Python dili üzerinde olduğu için nasıl bir çıktı verdirtsem diye düşünürken bir yandan da yapay zekalar TCP portu üzerinden soket mantığı ile çalışmam gerektiğini anlatıyordu kanımca bu fikir hiç hoşuma gitmedi ben de fazla duramadan geçmişte tanışmış olduğum Microsoft 'ta çalışan yazılım mühendisi abimize sordum o da bana Memory-Mapped File olayından bahsetti nedir diye kısaca özetleyecek olursak; bir dosyanın veya isimlendirilmiş bir bellek alanının, işletim sistemi tarafından sanal belleğe eşlenmesi anlamına gelir diğer tabirle bir dosyayı veya paylaşılan bir bellek alanını doğrudan RAM üzerinden erişilebilir hale getirir. Yani dosya okuma/yazma işlemleri klasik FileStream yerine bellek erişimi gibi yapılır & veri kopyalamadan, soket kullanmadan haberleşme yapılır. Bizde bugün burada onu yapacağız.
Tabi konumuza geçmeden önce bazı uyarıları yapmakta fayda var;
pip install opencv-pythonpy -3.10 -m pip install numpy==1.26.4py -3.10 -m pip install mediapipe==0.10.14py -3.10 -m pip install pywin32py -3.10 "C:\Users\user\AppData\Local\Programs\Python\Python310\Scripts\pywin32_postinstall.py" -install ( CMD Yönetici olarak çalıştır. )Uyarılarımız bittiğine göre Python kodumuz aşağıdadır;
Python:
import cv2
import mediapipe as mp # Yeni sürümlerde önerilen import şekli
import mmap
import struct
import win32event
# Shared memory ayarları
MMAP_SIZE = 4 + 4 + 63 * 4 # frame_id (int) + detected (int) + 21 landmark * 3 float
MMAP_NAME = "HandSharedMemory"
MUTEX_NAME = "HandMutex"
# Shared memory ve mutex oluştur
mm = mmap.mmap(-1, MMAP_SIZE, tagname=MMAP_NAME)
mutex = win32event.CreateMutex(None, False, MUTEX_NAME)
# Mediapipe Hands (daha güncel import)
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.7,
min_tracking_confidence=0.7
)
# Kamera aç
cap = cv2.VideoCapture(0)
# Frame ID sayacı
frame_id = 0
print("El takibi başladı. Çıkmak için 'q' tuşuna bas.")
while True:
ret, frame = cap.read()
if not ret:
print("Kamera hatası!")
break
# BGR -> RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Mediapipe ile işlem
results = hands.process(rgb_frame)
# Ekranda göster (isteğe bağlı)
annotated_frame = frame.copy()
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
mp.solutions.drawing_utils.draw_landmarks(
annotated_frame,
hand_landmarks,
mp_hands.HAND_CONNECTIONS
)
cv2.imshow('Hand Tracking - Gönderiliyor...', annotated_frame)
# Klavyeden çıkış
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# --- Shared Memory'ye yazma (mutex ile korunmuş) ---
win32event.WaitForSingleObject(mutex, -1) # INFINITE yerine -1 (çalışan çözüm)
try:
mm.seek(0)
frame_id += 1
mm.write(struct.pack("i", frame_id)) # 4 byte frame_id
if results.multi_hand_landmarks:
mm.write(struct.pack("i", 1)) # El tespit edildi
# Sadece ilk el (max_num_hands=1 olduğu için [0])
for landmark in results.multi_hand_landmarks[0].landmark:
mm.write(struct.pack("f", landmark.x))
mm.write(struct.pack("f", landmark.y))
mm.write(struct.pack("f", landmark.z))
else:
mm.write(struct.pack("i", 0)) # El yok
# 63 float sıfır yaz (yapıyı bozmamak için)
mm.write(b'\x00' * 63 * 4)
finally:
win32event.ReleaseMutex(mutex) # MUTEX'i serbest bırak! (ÇOK ÖNEMLİ)
# Temizlik
cap.release()
cv2.destroyAllWindows()
mm.close()
Burada işimiz bitti geçelim C# tarafına...
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.IO.MemoryMappedFiles;using System.Windows.Forms;using System.Linq;using System.Text;using System.Threading.Tasks;partial class altında gireceğimiz kodlar.
C#:
MemoryMappedFile mmf;
MemoryMappedViewAccessor acc;
readonly float[] lm = new float[63];
int handFlag;
static readonly (int, int)[] Bones =
{
(0,1),(1,2),(2,3),(3,4),
(0,5),(5,6),(6,7),(7,8),
(0,9),(9,10),(10,11),(11,12),
(0,13),(13,14),(14,15),(15,16),
(0,17),(17,18),(18,19),(19,20)
};
readonly Timer timer = new Timer();
// FPS
readonly Stopwatch fpsWatch = new Stopwatch();
int frameCount;
float fps;
// Gesture
string currentGesture = "None";
public form1 altında gireceğimiz kodlar.
C#:
InitializeComponent();
this.Load += Form1_Load;
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
BackColor = Color.Black;
try
{
mmf = MemoryMappedFile.OpenExisting("HandSharedMemory");
acc = mmf.CreateViewAccessor();
}
catch
{
MessageBox.Show("Shared memory bulunamadı.", "Hata",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Close();
return;
}
timer.Interval = 16; // ~60 FPS
timer.Tick += Timer_Tick;
timer.Start();
fpsWatch.Start();
Bir zamanlayıcı yani Timer tanımlayalım.
C#:
private void Timer_Tick(object sender, EventArgs e)
{
handFlag = acc.ReadInt32(0);
acc.ReadArray(4, lm, 0, 63);
if (lm.Any(v => float.IsNaN(v) || float.IsInfinity(v)))
return;
DetectGesture();
UpdateFPS();
Invalidate();
}
Gelen FPS değerini hesaplayalım.
C#:
void UpdateFPS()
{
frameCount++;
if (fpsWatch.ElapsedMilliseconds >= 1000)
{
fps = frameCount * 1000f / fpsWatch.ElapsedMilliseconds;
frameCount = 0;
fpsWatch.Restart();
}
}
Elimizi form üzerine çizdiren kodu girelim.
C#:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
if (handFlag == 0)
{
DrawOverlay(g);
return;
}
bool isRight = handFlag == 1;
Pen bonePen = new Pen(isRight ? Color.Cyan : Color.Magenta, 2);
Brush jointBrush = isRight ? Brushes.Lime : Brushes.Orange;
PointF[] pts = new PointF[21];
for (int i = 0; i < 21; i++)
{
float x = lm[i * 3] * Width;
float y = lm[i * 3 + 1] * Height;
float z = lm[i * 3 + 2];
x = Clamp(x, 0, Width);
y = Clamp(y, 0, Height);
pts[i] = new PointF(x, y);
float size = Math.Max(3f, 8f - z * 20f);
g.FillEllipse(jointBrush, x - size / 2, y - size / 2, size, size);
}
foreach (var bone in Bones)
g.DrawLine(bonePen, pts[bone.Item1], pts[bone.Item2]);
DrawOverlay(g);
}
static float Clamp(float value, float min, float max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
// =====================
// DEBUG OVERLAY
// =====================
void DrawOverlay(Graphics g)
{
Font f = new Font("Consolas", 10);
Brush textBrush = Brushes.White;
string handText;
switch (handFlag)
{
case 1:
handText = "Sağ El";
break;
case 2:
handText = "Sol El";
break;
default:
handText = "El Yönü Bilinmiyor";
break;
}
g.DrawString("FPS: " + fps.ToString("0.0"), f, textBrush, 10, 10);
g.DrawString(handText, f, textBrush, 10, 26);
g.DrawString("Gesture: " + currentGesture, f, textBrush, 10, 42);
}
Elimiz hangi pozisyonda onu görelim.
C#:
void DetectGesture()
{
if (IsFist())
currentGesture = "Yumruk Yaptın!";
else if (IsPinch())
currentGesture = "İyiyim Pozisyonu!";
else if (IsPoint())
currentGesture = "İşaret Pozisyonu!";
else
currentGesture = "Elin Açık";
}
bool IsFist()
{
return lm[8 * 3 + 1] > lm[5 * 3 + 1] &&
lm[12 * 3 + 1] > lm[9 * 3 + 1] &&
lm[16 * 3 + 1] > lm[13 * 3 + 1] &&
lm[20 * 3 + 1] > lm[17 * 3 + 1];
}
bool IsPinch()
{
float dx = lm[4 * 3] - lm[8 * 3];
float dy = lm[4 * 3 + 1] - lm[8 * 3 + 1];
float dist = (float)Math.Sqrt(dx * dx + dy * dy);
return dist < 0.05f;
}
bool IsPoint()
{
bool indexUp = lm[8 * 3 + 1] < lm[5 * 3 + 1];
bool othersDown =
lm[12 * 3 + 1] > lm[9 * 3 + 1] &&
lm[16 * 3 + 1] > lm[13 * 3 + 1] &&
lm[20 * 3 + 1] > lm[17 * 3 + 1];
return indexUp && othersDown;
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
timer.Stop();
acc.Dispose();
mmf.Dispose();
base.OnFormClosing(e);
}
Şimdi projelerimizi çalıştıralım ardından sonuçları gözleyelim. Python script 'ini çalıştırmak için CMD üzerinde şahsen py -3.10 C:\DİZİNİM\script.py kodunu kullanıyorum.
[ SONUÇ ]
Bu kodlar biraz daha geliştirilip belki sevgili @ARWENDA 'nın profilime bırakmış olduğu instagram platformunda yer alan parmak hareketine göre animasyon çizimi eklenebilir diye düşünmekteyim şahsen yapabileceğim bu kadar değerli yorumlarınızı bekliyorum.
Son düzenleme:



