إشارة الارتفاع تحدد اتجاه الصفوف، والثلاثية ذات24 بت مرتبة أزرق ثم أخضر ثم أحمر. [1]
الخلاصة السريعة
- في صورتنا ذات الأعمدة الثلاثة تشغل ألوان كل صف 9 بايتات، وتتبعه 3 بايتات حشو.
- حجم الصف المخزن في ملفينا 12 بايتا؛ بيانات الصفين 24، والملف الكامل 78.
- كتبنا الملف ذا الارتفاع +2 بصفنا السفلي أولا، والملف ذا الارتفاع −2 بصفنا العلوي أولا.
- أعادت المكتبة الألوان الستة بالترتيب نفسه من الملفين، مع اختلاف ترتيب بايتاتهما.
- الانتقال 9 بايتات في ضابطنا جعل أول لون في الصف الثاني أسود، وأزاح بقية القراءة.
ابدأ بستة ألوان محددة
اخترنا صورة من ثلاثة أعمدة وصفين. ألوان الصف العلوي بصيغة RGB هي (220,20,30) و(20,200,40) و(30,40,220). ألوان السفلي هي (240,180,20) و(160,30,180) و(20,180,190). هذه قيم مؤلفة وليست قراءة لصورة فوتوغرافية؛ أسماء العلوي والسفلي تصف النتيجة التي نريد رؤيتها.
أول لون علوي في اختيارنا هو (220,20,30). كتب البرنامج ثلاثيته 30,20,220، أي 1E 14 DC بالنظام الست عشري. وعند عرض البيانات كألوان أعدنا ترتيبها إلى الثلاثية الأصلية. احتفظنا بالقيم العشرية في البرنامج، وبالست عشرية في الرسم، ليمكن تتبع الخانة نفسها بالطريقتين.
تستكمل الصفوف إلى مضاعف حد LONG ببايتات صفرية. [2]
في ملفينا نحتاج 3 × 3 = 9 بايتات لون للصف، ثم نضيف ثلاثة أصفار ليصير المجموع 12. بعد صفين نحصل على 2 × 12 = 24 بايتا. الأصفار الثلاثة ليست لونا رابعا في الصورة التي اخترناها؛ عدد أعمدتها ثلاثة منذ البداية.
أنشئ الملفين ثم افتحهما
شغل البرنامج في مجلد لا يحتوي bmp-case، بعد توفير Pillow. نفذناه باستخدام Python 3.12.14 وPillow 12.3.0. ينشئ ملفين محليين بأسماء مختلفة ثم يقرأهما من القرص؛ لا يحتاج صورة جاهزة. المقارنة داخل assert تشمل القيم الثماني عشرة، لا أسماء الألوان التقريبية.
import struct
from pathlib import Path
from PIL import Image
top=[(220,20,30),(20,200,40),(30,40,220)]
bottom=[(240,180,20),(160,30,180),(20,180,190)]
out=Path("bmp-case");out.mkdir()
for h in (2,-2):
rows=[bottom,top] if h>0 else [top,bottom]
data=b"".join(bytes(v for rgb in row for v in rgb[::-1])
+b"\0"*3 for row in rows)
header=struct.pack("<2sIHHI",b"BM",78,0,0,54)
info=struct.pack("<IiiHHIIiiII",40,3,h,1,24,0,24,0,0,0,0)
path=out/("bottom.bmp" if h>0 else "top.bmp")
path.write_bytes(header+info+data)
with Image.open(path) as im:
pixels=list(im.convert("RGB").getdata())
print(h,len(path.read_bytes()),pixels)
assert pixels==top+bottom
raw=(out/"top.bmp").read_bytes()[54:]
wrong=[tuple(raw[y*9+x*3:y*9+x*3+3][::-1])
for y in range(2) for x in range(3)]
print("wrong",wrong)
assert wrong!=top+bottom [1] [2] [3]طبع التنفيذ 78 لكل ملف، وأعاد في المرتين القائمة top+bottom. في حقولنا كتبنا عرضا 3، ومستوى واحدا، وعمقا 24، وضغطا 0؛ ثم وضعنا حمولة من 24 بايتا. قارنا القيم بعد فكها من الرأس الفعلي، حتى لا نعتمد على أسماء متغيرات البرنامج وحدها.
اقرأ البداية واتجاه الصفوف
رأس الملف يحدد نوع BM وحجمه وإزاحة بداية بيانات البكسلات. [3]
رأس الملف الذي حزمناه طوله 14 بايتا، ويليه رأس معلومات طوله 40. لذلك تبدأ بيانات ألواننا عند 14 + 40 = 54، وينتهي الملف عند 54 + 24 = 78. وضعنا الحقلين المحجوزين بصفر في الملفين. الأرقام هنا تخص البنية التي أنشأناها؛ لا تجعل 54 بديلا عن قراءة الإزاحة في ملف آخر.
في bottom.bmp أعطينا الارتفاع +2، وبدأت البيانات بثلاثية اللون السفلي الأول: 14 B4 F0. وفي top.bmp أعطيناه −2، فبدأت البيانات بثلاثية العلوي الأول: 1E 14 DC. بقيت ألوان كل صف في ترتيبها الأفقي الأصلي. الاختلاف الذي صنعناه بين الملفين هو اختيار الصف الذي يخزن أولا.

دع القراءة الخاطئة تكشف الحشو
آخر جزء من البرنامج يتعمد قراءة حمولة top.bmp بخطوة 9 بدلا من 12. يقرأ الصف الأول كما اخترناه، ثم يبدأ الصف التالي من بايتات الحشو الثلاثة. لهذا صارت الثلاثية الأولى (0,0,0)؛ بعدها جاءت (240,180,20) ثم (160,30,180). لم تصل القراءة المختصرة إلى آخر لون سفلي.
لم نغير الملف في هذا الضابط، ولم نطلب من Pillow تفسيره خطأ. أنشأنا قارئا صغيرا خاصا بهذه الحمولة واستخدمنا فيه إزاحة خاطئة عمدا. بهذا نفصل بين ملف صحيح وقاعدة انتقال غير صحيحة في برنامج القراءة. الرسم الأول يبين موضع الأصفار الذي دخلت منه المشكلة.
راجع البايتات والصورة معا
أعدنا فتح الملفين وقارنا بايتاتهما، فكانت مختلفة، بينما تطابقت القيم اللونية المفكوكة. وكبرنا المعاينتين بأقرب جار لرؤية الخانات الست، دون إضافة ألوان انتقالية إلى اختبارنا. إذا طابقت نتائجك، فسترى الشبكة نفسها من ترتيبَي التخزين؛ وإذا ظهر الأسود في الصف الثاني فراجع خطوة القراءة قبل تغيير قيم الألوان.
هذه تجربة محدودة بملفينا BI_RGB ذوي 24 بت. لم نختبر لوحة ألوان أو أقنعة بتات أو ضغطا أو ملفات تالفة، والقارئ القصير ليس محللا عاما للصور. احتفظ بالملف الأصلي، وقيم الرأس المقروءة، ومصفوفة الألوان الناتجة معا؛ كل سجل يجيب عن جزء مختلف من الاختبار.
المراجع
أُعدّ هذا المقال بمساعدة الذكاء الاصطناعي وراجعته أدوات ذكاء اصطناعي. لا يُنسب إلى مراجع بشري أو خبير مستقل. منهجية المحتوى.
