6.18. الصور والمصفوفات ndarray¶
الفئة Image هي السطح السريع للعمل على البكسلات بالصيغة الأصلية للكاميرا: فكل دالة فيها تعمل مباشرة على مخزن الإطارات بصيغة البكسل الأصلية للكاميرا. أما الوحدة numpy فهي السطح العددي العام لكل ما عدا ذلك. وهناك دالتان تربطان بينهما:
image.Image.to_ndarray()-- تنسخ بكسلات الصورة إلىndarray.الباني (constructor) للفئة
image.Image-- يبني صورة جديدة انطلاقًا منndarray.
تتيح هاتان الدالتان معًا لأي تطبيق أن يلتقط إطارًا، ثم يسلّمه إلى 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:
|
العنصر |
التعيين لقيمة بكسل بحجم 8 بت |
|---|---|---|
|
|
|
|
|
|
|
|
|
مثال -- عرض إطار بتدرج الرمادي كمصفوفة 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 المكافئ. استعن بالجسر للعمليات التي لا توفرها مكتبة الصور بالفعل.