فتحنا السورس الحقيقي لـOpenCASA (GPL، Java) واستخرجنا الخوارزمية الكاملة: المعايرة، السرعات، التنعيم، والتصنيف — وطبّقناها على محرّكنا وشُفنا النتيجة.
من MotilityParams.java — القيم اللي بتتدخل مرة واحدة وتفضل ثابتة للجهاز:
| الإعداد | القيمة الافتراضية | المعنى |
|---|---|---|
micronPerPixel | 0.481 | المعايرة — كل بكسل = 0.481 ميكرون (تتغيّر حسب قوة العدسة) |
frameRate | 60 | إطار/ثانية (لتحويل المسافة لسرعة) |
vclMin | 10 µm/s | أقل من كده = ساكن (Immotile) |
strProgressMotility | 85 % | عتبة الاستقامة للتقدمية |
vapProgressMotility | 120 µm/s | عتبة سرعة المسار للتقدمية |
wSize | 4 | نافذة التنعيم (moving average) لحساب VAP |
فعلاً زي ما قلت — المعايرة قيمة ثابتة تُدخَل (micronPerPixel). هي مرتبطة بقوة العدسة + الكاميرا. OpenCASA حاطط 0.481 افتراضي. فالمحرّك بتاعنا محتاج نفس الإعداد ده عشان يطلّع السرعات بـµm/s.
// VCL — السرعة المنحنية: مجموع المسافات بين كل نقطتين متتاليتين distance = Σ dist(point[i], point[i-1]) VCL = distance × micronPerPixel / ((n-1)/frameRate) // µm/s // VSL — السرعة المستقيمة: مسافة أول نقطة لآخر نقطة VSL = dist(first, last) × micronPerPixel / ((n-1)/frameRate) // VAP — السرّ! سرعة المسار المُنعَّم (مش الخام) avgTrack = movingAverage(track, wSize=4) // تنعيم المسار VAP = VCL(avgTrack) // VCL بس على المسار المنعّم LIN = VSL/VCL × 100 WOB = VAP/VCL × 100 STR = VSL/VAP × 100 ALH = متوسط/أقصى مسافة عمودية من النقطة الخام للمسار المنعّم BCF = عدد مرات عبور المسار الخام للمسار المنعّم / الزمن // Hz
OpenCASA بينعّم المسار (moving average) ويحسب VAP منه، وبيصنّف التقدمية بـSTR=VSL/VAP و VAP — مش الإزاحة الخام. ده اللي كان ناقصنا: إحنا كنا بنصنّف على الخام (jittery) فالتقدمية فشلت.
// لكل مسار: if (VCL < vclMin) → Immotile else if (STR > 85% AND VAP > 120µm/s) → Progressive else → Non-progressive
بسيطة وواضحة. بس بتعتمد كلياً على إن المسارات نضيفة ومُعايَرة — وده اللي بيفرّق.
ضفنا التنعيم (moving average) + VAP + تصنيف STR + المعايرة (0.481) لمحرّكنا وأعدنا التشغيل على الحالتين:
| الحالة | التقرير (PR) | 8 ثواني | معادلات OpenCASA | نافذة 1 ثانية | ربط تنبّؤي |
|---|---|---|---|---|---|
| 164 | 36% | 2% | 1% | 0% | 0% |
| 176 | 35% | 5% | 2% | 1% | 0% |
8 ثواني، معادلات OpenCASA، نافذة 1 ثانية، ربط تنبّؤي — كلها أعطت PR ≈ 0-2% مقابل 35%. يعني المشكلة مش النافذة ولا المعادلات — هي جودة الكشف والتتبّع:
micronPerPixel + frameRate (قيمتين ثابتتين تتدخلوا).micronPerPixel من العدسة).محرّكنا الأولي وصل سقفه: يحسّ الحركة الكلية تقريبياً + يطلّع صورة توثيق، لكن PR/NP و kinematics محتاجين محرّك تتبّع مبني من الأول (مشروع CV حقيقي — أسابيع/شهور)، مش تعديلات. التركيز/الشكل/الحيوية أصلاً مش من فيديو الحركة. التوصية العملية: نكمّل بالبنل اليدوي (جاهز) + نستخدم المحرّك كـمساعد للحركة وتوثيق بصري بس، ولو عايزين CASA آلي كامل → مشروع منفصل بكاميرا 60fps phase-contrast ومهندس رؤية حاسوبية.
قرينا السورس لفهم الفكرة ونعيد تنفيذها بكودنا (clean-room) — الخوارزميات نفسها منشورة في معايير WHO. ده مسموح؛ الممنوع هو نسخ/لينك كود OpenCASA جوّه برنامجنا المغلق (GPL).