6.18. الصور والمصفوفات ndarray

الفئة Image هي السطح السريع للعمل على البكسلات بالصيغة الأصلية للكاميرا: فكل دالة فيها تعمل مباشرة على مخزن الإطارات بصيغة البكسل الأصلية للكاميرا. أما الوحدة numpy فهي السطح العددي العام لكل ما عدا ذلك. وهناك دالتان تربطان بينهما:

تتيح هاتان الدالتان معًا لأي تطبيق أن يلتقط إطارًا، ثم يسلّمه إلى numpy لإجراء تحويل مخصص، ثم يعيد النتيجة إلى صورة لعرضها أو حفظها أو إعادة إدخالها إلى بقية مكتبة الصور.

6.18.1. من صورة إلى مصفوفة ndarray

تخصص الدالة to_ndarray() مصفوفة ndarray جديدة وتنسخ بيانات بكسلات الصورة إليها (بحسب تخطيط الـ dtype أدناه). وهي ليست أبدًا عرضًا (view) على مخزن إطارات الصورة -- فمصفوفة numpy تملك دائمًا بايتاتها الخاصة. والتوقيع هو to_ndarray(dtype, *, buffer=None)، ويعتمد شكل المخرجات على صيغة الصورة:

  • GRAYSCALE -- مصفوفة ثنائية الأبعاد، شكلها (height, width).

  • RGB565 -- مصفوفة ثلاثية الأبعاد، شكلها (height, width, 3)، والمستويات بترتيب R/G/B.

تتحكم الوسيطة dtype في كيفية تعيين كل قيمة بكسل بحجم 8 بت v:

dtype

العنصر

التعيين لقيمة بكسل بحجم 8 بت v

'B'

uint8

v (خام)

'b'

int8

v - 128 (معاد توسيطها حول الصفر)

'f'

float32

float(v) (من 0.0 إلى 255.0)

مثال -- عرض إطار بتدرج الرمادي كمصفوفة uint8

import csi
from ulab import numpy as np

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)

img = csi0.snapshot()
a   = img.to_ndarray('B')        # shape (240, 320), dtype=uint8

print(a.shape, a.dtype)
print("mean brightness:", np.mean(a))

تتيح الكلمة المفتاحية buffer= للتطبيق إعادة استخدام bytearray سبق أن خصصه، بحيث لا تضطر الكاميرا إلى تخصيص واحد جديد في كل إطار:

buf = bytearray(320 * 240)
while True:
    img = csi0.snapshot()
    a   = img.to_ndarray('B', buffer=buf)
    # ... process a ...

6.18.2. من مصفوفة ndarray إلى صورة

وفي الاتجاه المعاكس، مرّر المصفوفة ndarray كوسيطة أولى إلى image.Image. يخصص الباني مخزنًا جديدًا للصورة وينسخ قيم المصفوفة إليه، بعد قصّها وتقريبها إلى المدى 0..255

image.Image(arr, *, buffer=None, copy_to_fb=False)

يستنتج الباني الهندسة وصيغة البكسل من شكل المصفوفة:

  • الشكل (h, w) -- صورة GRAYSCALE.

  • الشكل (h, w, 3) -- صورة RGB565.

يجب أن يكون لدى المصفوفة ndarray نوع dtype من float؛ فالباني لا يدعم سوى هذه الحالة حاليًا. وتُقرَّب القيم وتُقَصّ إلى المدى 0..255.

تتيح الكلمة المفتاحية buffer= للتطبيق توفير bytearray سبق أن خصصه للصورة الناتجة. أما copy_to_fb=True فتكتب النتيجة في مخزن إطارات الكاميرا، وهو الخيار الصحيح عندما يُراد أن تظهر النتيجة في معاينة الـ IDE.

6.18.3. الرحلة ذهابًا وإيابًا

import csi
import image
from ulab import numpy as np

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)

img = csi0.snapshot()
a   = img.to_ndarray('f')                  # work in float space

a = 255.0 * (a / 255.0) ** 0.5             # gamma correction

out = image.Image(a, copy_to_fb=True)      # back to an image

6.18.4. متى نستخدم الجسر

هذا الجسر هو الجواب الصحيح عندما يحتاج التطبيق إلى عملية عددية عامة لا توفرها دوال image المدمجة -- مرشحات مخصصة، أو مزج مخصص، أو لاخطّيات غير معتادة -- أو عندما يتعين دمج بيانات البكسل مع بيانات غير صورية (محاور IMU، أو عينات صوتية) في حساب واحد.

وهو ليس الجواب الصحيح لمعالجة البكسلات عالية الإنتاجية التي تغطيها الفئة Image بالفعل. فالدوال المدمجة تعمل مباشرة على مخزن الإطارات بصيغة البكسل الأصلية للكاميرا، وهي أسرع بكثير من تعبير numpy المكافئ. استعن بالجسر للعمليات التي لا توفرها مكتبة الصور بالفعل.