5.32. الحفظ والضغط

تعاملت كل صفحة حتى الآن مع الصور على الكاميرا: ملتقطة في مخزن الإطارات أو مخصصة على كومة MicroPython، ومعالَجة عبر دوال وحدة image، ومعروضة في معاينة الـ IDE أو مُمررة إلى مرحلة لاحقة في البرنامج النصي نفسه. تحتاج معظم التطبيقات في مرحلة ما إلى فعل العكس: أخذ صورة موجودة حالياً في ذاكرة RAM ووضعها في مكان دائم -- على بطاقة SD، أو على مضيف USB، أو عبر شبكة -- حيث يمكن لشيء آخر غير الكاميرا قراءتها.

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

5.32.1. الحفظ إلى ملف

تكتب save() الصورة إلى نظام الملفات عند مسار:

img.save("/sdcard/capture.jpg")
img.save("/sdcard/capture.bmp")
img.save("/sdcard/region.jpg", roi=(40, 60, 200, 150), quality=85)

تُختار الصيغة من امتداد الملف. يُتعرَّف على خمسة امتدادات: .bmp يكتب صورة نقطية لـ Windows (بلا فقدان، بلا ضغط، بكسلات ملتقطة بايتاً ببايت)؛ و.pgm يكتب خريطة رمادية محمولة (بلا فقدان، تدرج رمادي فقط)؛ و.ppm يكتب خريطة بكسل محمولة (بلا فقدان، RGB)؛ و.jpg و.jpeg كلاهما يكتب JPEG (مع فقدان، مضغوط). يجب أن تكون الصورة المستقبِلة بالفعل في صيغة اللون الصحيحة للحاوية المختارة -- فحفظ صورة ملونة كـ .pgm خطأ.

يقيّد roi الحفظ على مستطيل فرعي من الصورة، تماماً كما تفعل كلمة roi المفتاحية في كل دالة أخرى من وحدة image. الصورة الكاملة هي الافتراضي. تُتجاهل الكلمة المفتاحية عند حفظ صورة مضغوطة بـ JPEG لأن الصيغة على القرص تغطي بالفعل الإطار الكامل، وإعادة الترميز عبر اقتصاص ستُفشل الغرض من حفظ البايتات المضغوطة الموجودة.

quality هي جودة ضغط JPEG من 0 إلى 100 ولها معنى فقط عندما يكون الخرج JPEG (تُتجاهل الكلمة المفتاحية للصيغ بلا فقدان). القيمة الافتراضية 50 هي التوازن الصحيح لـمعظم التطبيقات؛ و70 إلى 85 هو النطاق لجودة بصرية أعلى، و30 إلى 50 هو النطاق الصحيح للصور المصغرة والإرسال المقيد بعرض النطاق، و90 فما فوق محجوز للحالات التي ستُفحص فيها الصورة يدوياً أو تُمرَّر عبر خوارزمية لاحقة حساسة لتشوهات الضغط.

تُرجَع الصورة المستقبِلة بحيث يتسلسل الاستدعاء: img.save("/sdcard/x.jpg").draw_string(0, 0, "saved"). الكائن المُرجَع هو الصورة نفسها في الذاكرة؛ والحفظ تأثير جانبي.

من الاستخدامات النمطية نمط الالتقاط والتسجيل. يُطلق مُحفِّز (تُكتشف كتلة، أو يُضغط زر، أو ينقضي مؤقت)؛ فيلتقط البرنامج النصي إطاراً؛ ويُلحق طابعاً زمنياً باسم الملف؛ ويستدعي save() لدفع الصورة إلى بطاقة SD. تستمر معاينة الـ IDE في العمل، ويُطلق المُحفِّز التالي، وتتراكم الملفات المحفوظة.

5.32.2. الترميز إلى الذاكرة

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

encoded = img.to_jpeg(quality=80, copy=True)
bytes_to_send = encoded.bytearray()
sock.send(bytes_to_send)

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

quality وsubsampling هما مقبضا ضبط JPEG نفساهما اللذان يكشفهما مسار الحفظ. يختار subsampling نظام تقليل أخذ عينات اللونية (chroma): image.JPEG_SUBSAMPLING_AUTO يختار الأفضل للجودة المختارة، وimage.JPEG_SUBSAMPLING_444 يبقي اللونية بدقة كاملة (أكبر ملف، أفضل دقة لونية)، وimage.JPEG_SUBSAMPLING_422 وimage.JPEG_SUBSAMPLING_420 يُنصِّفان دقة اللونية على محور واحد أو على المحورين (ملفات أصغر، تنعيم لوني طفيف غير مرئي على مسافات المشاهدة النمطية). القيمة الافتراضية AUTO هي الخيار الصحيح ما لم يكن للتطبيق حاجة محددة.

PNG عبر to_png() بلا فقدان لكنه أبطأ في الترميز ويُنتج ملفات أكبر من JPEG للمحتوى الفوتوغرافي (المحتوى الفوتوغرافي يُضغط بشكل سيئ تحت نظام التنبؤ الخاص بـ PNG). استخدم PNG عندما تكون الصورة رسماً خطياً أو لقطة شاشة أو تحتوي على رسوميات حادة الحواف مرسومة فوق إطار ملتقط -- فالترميز بلا فقدان يحافظ على الحواف الحادة التي قد يُنعِّمها JPEG. وإلا فإن JPEG هو الافتراضي الصحيح.

تقبل كل من to_jpeg() وto_png() الكلمات المفتاحية الموضعية وكلمات القياس بأسلوب الرسم نفسها التي تأخذها دوال التحويل الأخرى -- x_scale وy_scale وroi وrgb_channel وalpha وcolor_palette وalpha_palette وhint -- بحيث يمكن للاستدعاء نفسه أن يُرمِّز نسخة مُقاسة أو مقتصَّة أو مُعيَّنة بلوحة ألوان من المصدر في خطوة واحدة. compress() هي التهجئة القديمة لـ to_jpeg()؛ وتأخذ الاثنتان نفس الوسائط وتُنتجان نفس النتيجة.

5.32.3. ما يوفره الضغط

الأرقام وراء مقايضة JPEG مقابل الخام تستحق العمل عليها مرة واحدة.

إطار RGB565 بحجم 320 في 240 يبلغ 153,600 بايت (إطار واحد ملتقط بدقة QVGA). وإطار 640 في 480 يبلغ 614,400 بايت؛ وإطار 1280 في 960 يبلغ 2,457,600 بايت. لا شيء من ذلك كبير مقارنة بشاشة سطح مكتب أو هاتف، لكنها كبيرة في سياق كاميرا تملك بضعة ميغابايتات من ذاكرة RAM إجمالاً، وبطاقة SD ذات عرض نطاق كتابة محدود، ووصلة مضيف تعمل عادةً عبر USB CDC أو UART أو وحدة لاسلكية بسرعات متواضعة.

JPEG عند quality=50 يضغط عادةً إطاراً فوتوغرافياً ملتقطاً بمقدار 10 إلى 20 مرة: فيصبح ذلك الإطار 640 في 480 بحجم 614 كيلوبايت دفقَ بايتات مُرمَّزاً بحجم 30 إلى 60 كيلوبايت. وعند quality=85 ينخفض الضغط إلى 5 إلى 10 مرات (60 إلى 120 كيلوبايت للإطار نفسه). وعند quality=10 -- مليء بالتشوهات لكنه ما زال قابلاً للتمييز -- يصل الضغط إلى 30 إلى 50 مرة (12 إلى 20 كيلوبايت).

تحدد تلك الأرقام ما هو عملي فعله بالإطارات المحفوظة. مسار بطاقة SD يحافظ على 10 ميغابايت/ثانية يتعامل مع 30 إطاراً في الثانية من محتوى VGA مُرمَّز بـ JPEG عند quality=50 مع متسع للزيادة (نحو 1 إلى 2 ميغابايت/ثانية)؛ بينما حفظ المحتوى نفسه دون ضغط يتطلب أكثر من 18 ميغابايت/ثانية، وهو ما يتجاوز ما يحافظ عليه مسار نظام ملفات الكاميرا إلى البطاقة. مضيف USB يسحب إطارات مُرمَّزة بـ JPEG عبر CDC بسرعة 1 ميغابايت/ثانية يستقبل إطارات بحجم 30 إلى 60 كيلوبايت بمعدل نحو 15 إلى 30 إطاراً في الثانية؛ بينما سحب إطارات خام بالمعدل نفسه يحصل على إطار أو إطارين في الثانية.

باختصار: دوال الضغط ليست مجرد راحة للحفظ. إنها ما يجعل الإطار الملتقط قابلاً للاستخدام خارج الكاميرا بمعدلات إطارات يهتم بها التطبيق. واختيار الضغط الصحيح -- جودة JPEG 50 للتسجيل العام، و80 للعمل الجيد، وPNG لالتقاط الرسوم الخطية -- جزء من العمل الروتيني لأي تطبيق كاميرا غير تافه.