4.16. معاينة الصورة

تجمّع مخازن الإطارات هو المكان الذي يقرأ منه التطبيق إطاراته. وبينما يعمل التطبيق على تلك الإطارات، يحتاج أي شيء متصل بالكاميرا لمعاينتها إلى نسخة من كل إطار أيضاً. تمتلك الكاميرا مخزناً ثانياً مخصصاً لهذا الغرض، وقاعدة واحدة لوقت ملئه: في كل مرة يستدعي فيها التطبيق snapshot()، يُنسخ الإطار الملتقط السابق إلى مخزن المعاينة قبل تسليم الإطار الجديد.

لا يتنافس التطبيق والمُعايِن أبداً على الذاكرة نفسها. يقرأ التطبيق إطاره من التجمّع؛ ويقرأ المُعايِن إطاره من مخزن المعاينة. ويحدث كلاهما بالتوازي.

4.16.1. مخزن إطارات الدفق

مخزن المعاينة -- مخزن إطارات الدفق -- هو منطقة واحدة ثابتة الحجم من الذاكرة RAM منفصلة عن تجمّع مخازن الإطارات. يُحدَّد حجمه وقت بناء البرنامج الثابت ولا يتغير مع framesize() أو pixformat(). يبلغ نحو ميغابايت واحد على كاميرات OpenMV Cam الحديثة عادةً -- كبير بما يكفي لاحتواء معاينة بدقة معتدلة، وأصغر بكثير من إطار بدقة كاملة عند أكبر أحجام المستشعرات.

لا يقرأ كود التطبيق هذا المخزن أو يكتب فيه مباشرة؛ بل يملؤه برنامج تشغيل الكاميرا كأثر جانبي لـ snapshot().

4.16.2. ماذا تفعل اللقطة من أجل المعاينة

عند كل استدعاء لـ snapshot()، وقبل أن يُحرر برنامج التشغيل مخزن الإطارات السابق للتطبيق ويعيده إلى التجمّع ويسلِّم الإطار الجديد، فإنه ينسخ الإطار السابق إلى مخزن المعاينة -- مع كل ما رسمه التطبيق فوقه أثناء المعالجة وهو لا يزال على الصورة. يمكن أن يحدث فرعان. وأي منهما ينفَّذ يختاره المُعايِن، لا الكاميرا: يُخبر المستهلك الذي فتح المعاينة برنامج التشغيل بما إذا كان يريد الصورة الخام أم JPEG، وما حجم النافذة الخام الذي يمكنه قبوله.

  • نسخة خام مُصغَّرة الحجم. عندما يكون المُعايِن قد طلب إطارات خام، ينسخ برنامج التشغيل الإطار السابق بتنسيق البكسل الأصلي له (RGB565، تدرج الرمادي، إلخ). إذا كان الإطار أكبر من النافذة الخام التي طلبها المُعايِن، يُصغِّره برنامج التشغيل بترشيح ثنائي الخطية حتى يتسع؛ وإلا فتمر البكسلات دون تغيير. لا توجد عيوب ضغط؛ ويرى المُعايِن البكسلات نفسها التي كان التطبيق يعمل عليها.

  • ضغط JPEG. عندما يكون المُعايِن قد طلب JPEG -- أو عندما لا تتسع النسخة الخام في مخزن الدفق على الإطلاق -- يضغط برنامج التشغيل الإطار السابق بصيغة JPEG بدقته الكاملة داخل مخزن الدفق. تُعدَّل الجودة تكيفياً لكل إطار بحيث يظل الخرج المضغوط ضمن سعة مخزن الدفق. عندما يتسع إطار، يرفع برنامج التشغيل الجودة تدريجياً بخطوة واحدة نحو سقف يعتمد على حجم بكسل الإطار الملتقط (يُسمح للإطارات الأصغر بجودة أعلى؛ وتُقيَّد الإطارات الأكبر بحد أدنى بحيث لا يمكن أن تفيض عند تغيير محتوى صغير). وعندما لا يتسع إطار، يخفض برنامج التشغيل الجودة الحالية إلى النصف، ويثبتها عند المستوى المنخفض لعشرات الإطارات التالية بحيث يتاح للإعداد الجديد وقت للاستقرار، ويُسقط الإطار الذي فاض من المعاينة. تستمر حلقة التطبيق في العمل دون تأثر؛ والمُعايِن وحده هو من يفوته الإطار المُسقَط.

الإطارات التي تنتجها الكاميرا بتنسيق مضغوط بالفعل (تنسيق بكسل JPEG على المستشعرات التي تُصدر JPEG مباشرة) تتخطى كلا الفرعين: تُنسخ سلسلة البتات المُرمَّزة مباشرة إلى مخزن المعاينة كما هي.

يستقصي المُعايِن وفق جدوله الزمني الخاص، وهو عموماً أبطأ بكثير من معدل التقاط الكاميرا، فيأخذ عينات فرعية من معدل الالتقاط الخام: تُعرض فقط اللقطات التي صادف أن قرأها في الوقت المناسب. إذا وصلت snapshot() جديدة إلى مخزن المعاينة قبل أن يكون المُعايِن قد قرأ الإطار السابق، يكون المخزن لا يزال مقفلاً من قبل المُعايِن ويُتخطى تحديث المعاينة الجديد -- ويُفقد ذلك الالتقاط من دفق المعاينة. ولا يتأثر تجمّع مخازن إطارات التطبيق نفسه؛ ولا يزال الإطار الملتقط يذهب إلى التطبيق بشكل طبيعي.

4.16.3. دفع الإطار الأخير يدوياً

لأن المعاينة تُحدَّث كأثر جانبي لـ snapshot()، فإن البرنامج النصي الذي ينتهي دون أن يستدعي اللقطة مرة أخرى أبداً يترك ما أرسله آخر مرة إلى المعاينة جالساً على المُعايِن إلى أجل غير مسمى -- وهو ما يكون، بالنسبة لبرنامج نصي ينجز عمله قبل أول لقطة ثم يخرج، معاينة فارغة. ينسخ image.Image.flush() (أو المكافئ flush() على كائن CSI) المحتويات الحالية لمخزن إطارات التطبيق إلى مخزن الدفق عند الطلب، دون التقاط إطار جديد:

img = csi0.snapshot()
# process the image and draw on it
img.flush()                               # previewer sees the annotated frame

الاستدعاء نفسه مفيد أيضاً عندما تقع عملية طويلة الأمد بين اللقطات ولولاه لعرض المُعايِن معاينة قديمة طوال ذلك الوقت.

ملاحظة

على تطبيق المعاينة أن يقرأ الإطار من مخزن الدفق قبل أن يخرج البرنامج النصي. إن استدعاء flush في نهاية برنامج نصي قصير يهيئ الإطار فقط؛ فإذا أعاد البرنامج النصي بعد ذلك التحكم إلى الكاميرا قبل أن يكون المُعايِن قد استقصى، يُعاد استخدام المخزن في التشغيل التالي ويُفقد ذلك الإطار الأخير. بالنسبة لمعاينات نهاية البرنامج النصي، امنح المُعايِن لحظة لالتقاط الإطار (سكون قصير بعد flush، أو ببساطة عدم الخروج فوراً) قبل أن ينتهي البرنامج النصي.