العمل مع الصور في Python

موضوع محادثة اليوم هو ما تعلمته بايثون طوال سنوات وجودها في العمل مع الصور. في الواقع ، بالإضافة إلى الإصدارات القديمة من 1990 ImageMagick و GraphicsMagick ، ​​هناك مكتبات فعالة حديثة. على سبيل المثال ، وسادة وسادة أكثر إنتاجية- SIMD. قام المطور النشط ألكسندر كاربينسكي ( homm ) في MoscowPython بمقارنة المكتبات المختلفة للعمل مع الصور في Python ، وقدم معايير مرجعية وتحدث عن الميزات غير الواضحة التي تكفي دائمًا. في هذه المقالة ، سيساعدك نص التقرير في اختيار مكتبة لتطبيقك ، وجعله يعمل بأكبر قدر ممكن من الكفاءة.


نبذة عن المتحدث: يعمل ألكسندر كاربينسكي في Uploadcare ويعمل في خدمة تعديل الصورة بشكل سريع. وهو يشارك في تطوير وسادة ، مكتبة شعبية للعمل مع الصور في Python ، ويقوم بتطوير شوكة خاصة به من هذه المكتبة ، وسادة - SIMD ، والتي تستخدم تعليمات المعالج الحديثة لتحقيق أقصى أداء.

الخلفية


خدمة تعديل الصور في Uploadcare هي خادم يتلقى طلب HTTP بمعرف صورة وبعض العمليات التي يحتاج العميل إلى تنفيذها. يجب على الخادم إكمال العمليات والاستجابة في أسرع وقت ممكن. غالبًا ما يعمل العميل كمتصفح.

يمكن وصف الخدمة بأكملها على أنها غلاف حول مكتبة الرسومات. تعتمد جودة المشروع بأكمله على جودة وأداء وسهولة استخدام مكتبة الرسومات. من السهل تخمين أن برنامج Uploadcare يستخدم الوسادة كمكتبة رسومات.

مكتبات


سننظر بإيجاز في نوع مكتبات الرسومات الموجودة في Python من أجل فهم أفضل لما سيتم مناقشته لاحقًا.

وسادة


وسادة - شوكة PIL (مكتبة التصوير في Python). هذا مشروع قديم جدًا تم إصداره عام 1995 لـ Python 1.2. يمكنك أن تتخيل كم عمره! في مرحلة ما ، تم التخلي عن مكتبة تصوير Python وتوقف تطورها. تم عمل شوكة وسادة لتثبيت وبناء مكتبة التصوير Python على الأنظمة الحديثة. تدريجياً ، ازداد عدد التغييرات التي يحتاجها الأشخاص في مكتبة تصوير Python ، وخرج وسادة 2.0 ، مما أضاف دعمًا لـ Python 3. يمكن اعتبار هذا بداية حياة منفصلة لمشروع الوسادة.

الوسادة هي وحدة أصلية لـ Python ، نصف الرمز مكتوب في C ، ونصف في Python. الإصدارات الأكثر تنوعًا من Python مدعومة: 2.7 ، 3.3+ ، PP ، .

وسادة- SIMD


هذا هو شوكة الوسادة الخاصة بي ، والتي ستصدر في مايو 2016. يشير SIMD إلى التعليمات الفردية ، البيانات المتعددة   - نهج يمكن للمعالج من خلاله تنفيذ عدد أكبر من الإجراءات لكل دورة باستخدام التعليمات الحديثة.

وسادة - SIMD ليست شوكة بالمعنى الكلاسيكي عندما يبدأ المشروع في أن يعيش حياته الخاصة. هذا بديل للوسادة ، أي أنك تقوم بتثبيت مكتبة بدلاً من مكتبة أخرى ، ولا تغير سطرًا في التعليمات البرمجية المصدر ، وتحصل على المزيد من الأداء.

يمكن تجميع وسادة SIMD مع تعليمات SSE4 (افتراضي). هذه مجموعة من التعليمات الموجودة في جميع معالجات x86 الحديثة تقريبًا. يمكن أيضًا تجميع وسادة SIMD مع مجموعة تعليمات AVX2. هذه المجموعة من التعليمات ، بدءًا من هندسة Haswell ، أي تقريبًا من عام 2013.

Opencv


مكتبة أخرى للعمل مع الصور في Python ربما سمعت عنها هي OpenCV (Open Computer Vision). انها تعمل منذ عام 2000. يتم تضمين ربط بايثون. وهذا يعني أن الربط وثيق الصلة باستمرار ، ولا يوجد التزامن بين المكتبة نفسها والربط.

لسوء الحظ ، هذه المكتبة غير مدعومة في PyPy ، لأن OpenCV مبني على numpy ، و numpy بدأ العمل مؤخرًا فقط تحت PyPy ، و PyC لا يدعم OpenCV بعد.

فيبس


مكتبة أخرى تستحق الانتباه هي VIPS. الفكرة الرئيسية لـ VIPS هي أنك لا تحتاج إلى تحميل الصورة بالكامل في الذاكرة للعمل مع الصورة. يمكن للمكتبة تحميل بعض القطع الصغيرة ومعالجتها وحفظها. وبالتالي ، لمعالجة صور gigapixel ، لا تحتاج إلى إنفاق جيجابايت من الذاكرة.

هذه مكتبة قديمة إلى حد ما - 1993 ، لكنها تجاوزت وقتها. لفترة طويلة ، لم يكن هناك الكثير مما سمع عنه ، ولكن في الآونة الأخيرة ، بدأ ظهور روابط VIPS للغات المختلفة ، بما في ذلك Go ، Node.js ، Ruby.

لفترة طويلة أردت تجربة هذه المكتبة ، لأشعر بها ، لكنني لم أنجح لسبب غبي للغاية. لم أستطع معرفة كيفية تثبيت VIPS ، لأن الربط كان معقدًا للغاية. ولكن الآن (في عام 2017) تم إصدار تجليد pyvips من مؤلف VIPS نفسه ، والذي لم يعد هناك أي مشاكل. أصبح تثبيت VIPS واستخدامه أمرًا سهلاً الآن. مدعوم: Python 2.7 ، 3.3+ ، RuPu ، RuPuZ.

ImageMagick & GraphicsMagick


إذا تحدثنا عن العمل مع الرسومات ، فلا يسعنا إلا أن نذكر كبار السن - مكتبات ImageMagick و GraphicsMagick . كان هذا الأخير في الأصل شوكة من ImageMagick بأداء أكبر ، ولكن يبدو الآن أن أدائهم متساوي. على حد علمي ، لا توجد اختلافات جوهرية أخرى بينهما. لذلك ، يمكنك استخدام أي ، بشكل أكثر دقة ، الذي تفضل استخدامه.

هذه أقدم المكتبات التي ذكرتها اليوم (1990). خلال كل هذا الوقت ، كان هناك العديد من المجلدات لـ Python ، وقد مات جميعهم تقريبًا بأمان حتى الآن. من بين تلك التي يمكن استخدامها ، هناك:

  • ربط العصا ، الذي بني على أنواع ، ولكن لم يعد يتم تحديثه أيضًا.
  • يستخدم ربط pgmagick Boost.Python ، لذلك يتم تجميعه لفترة طويلة جدًا ولا يعمل في PyPy. ولكن ، مع ذلك ، يمكنك استخدامه ، أود أن أقول أنه أفضل من Wand.

الأداء


عندما نتحدث عن العمل مع الصور ، فإن أول ما يهمنا (على الأقل بالنسبة لي) هو الأداء ، لأنه بخلاف ذلك يمكننا كتابة شيء ما في بايثون بأيدينا.

الأداء ليس بهذه البساطة. لا يمكنك القول أن مكتبة واحدة أسرع من مكتبة أخرى. تحتوي كل مكتبة على مجموعة من الوظائف ، وتعمل كل وظيفة بسرعة مختلفة.

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

من المهم التحقق من النتيجة


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

في الآونة الأخيرة ، في مقالة تقارن أداء وسادة و OpenCV ، صادفت هذا الرمز:

from PIL import Image, ImageFilter.BoxBlur im.filter(ImageFilter.BoxBlur(3)) ... import cv2 cv2.blur(im, ksize=(3, 3)) ... 

يبدو أن هناك ، وهناك ، BoxBlur ، وهناك ، والحجة 3 ، ولكن في الواقع كانت النتيجة مختلفة. لأنه في الوسادة (3) هذا هو نصف قطر التمويه ، وفي OpenCV ksize = (3 ، 3) هو حجم النواة ، أي القطر تقريبًا. في هذه الحالة ، ستكون القيمة الصحيحة لـ OpenCV هي 3 * 2 + 1 ، أي (7 ، 7).

ما هي المشكلة؟


لماذا يعتبر الأداء بشكل عام مشكلة عند العمل مع الرسومات؟ لأن تعقيد أي عملية يعتمد على عدة معلمات ، وغالبًا ما ينمو التعقيد بشكل خطي مع كل منها. وإذا كان ، على سبيل المثال ، هناك ثلاثة من هذه العوامل ، ويعتمد التعقيد خطياً على كل منها ، يتم الحصول على التعقيد في المكعب.

مثال: التمويه الغوسي في OpenCV.



على اليسار نصف قطر 3 ، على اليمين 30. كما ترون ، الفرق في السرعة أكثر من 10 مرات.

عندما واجهت مهمة إضافة طمس غاوسي إلى طلبي ، لم أكن سعيدًا ، من الناحية الافتراضية ، يمكن إنفاق 900 مللي ثانية على عملية واحدة. هناك الآلاف من هذه العمليات في الدقيقة في التطبيق ، وإنفاق الكثير من الوقت على واحدة غير عملي. لذلك ، درست القضية وقمت بتطبيق التمويه الغوسي في الوسادة ، والتي تعمل في وقت ثابت بالنسبة إلى نصف القطر. أي أن حجم الصورة فقط يؤثر على أداء التمويه الغاوسي.

لكن الشيء الرئيسي هنا ليس أن شيئًا ما يعمل بشكل أسرع أو أبطأ.

أريد أن أوضح أنه عندما تبني نوعًا من النظام ، من المهم أن تفهم المعلمات التي يعتمد عليها تعقيد المخرجات. ثم يمكنك تحديد هذه المعلمات أو بطرق أخرى للتعامل مع هذا التعقيد.

ربما تكون العملية الأكثر شيوعًا التي نقوم بها مع الصور بعد فتحها هي تغيير الحجم.



يوضح الرسم البياني أداء المكتبات المختلفة (أكثر هو الأفضل) لعملية تقليل الصورة بمقدار 8 و 1.25 مرة.

بالنسبة لـ PIL ، تعني نتيجة 17 ميجا بكسل / ثانية أنه يمكن تقليل الصورة من iPhone (12 ميجا بكسل) 1.25 مرة قليلاً في أقل من ثانية. هذا الأداء لا يكفي لتطبيق جاد يؤدي الكثير من هذه العمليات.

بدأت في تحسين أداء تغيير الحجم ، وفي الوسادة 2.7 تمكنت من تحقيق زيادة مضاعفة في الإنتاجية ، وفي الوسادة 4.3 - ثلاثة أضعاف (إصدار الوسادة 5.3 ملائم حاليًا ، ولكن أداء تغيير الحجم هو نفسه).

لكن عملية تغيير الحجم هي شيء يناسب بشكل جيد للغاية على SIMD. يقترب من تعليمات فردية وبيانات متعددة ، وبالتالي ، في الإصدار الحالي من Pillow-SIMD ، تمكنت من زيادة سرعة تغيير الحجم بمقدار 19 مرة مقارنة بمكتبة Python Imaging Library الأصلية باستخدام نفس الموارد.

هذا أعلى بكثير من أداء تغيير حجم OpenCV. لكن المقارنة ليست صحيحة تمامًا ، لأن OpenCV يستخدم طريقة أقل جودة بقليل لتغيير الحجم باستخدام مرشح صندوق ، وفي وسادة - SIMD ، يتم تنفيذ تغيير الحجم باستخدام الالتفافات.

هذه قائمة غير مكتملة لتلك العمليات التي يتم تسريعها في Pillow-SIMD مقارنة بالوسادة العادية.

  • تغيير الحجم: 4 إلى 7 مرات.
  • طمس: 2.8 مرة.
  • تطبيق النواة 3 × 3 أو 5 × 5: 11 مرة.
  • الضرب والقسمة بقناة ألفا: 4 و 10 مرات.
  • تكوين ألفا: 5 مرات.

سبق لي أن قلت أنه لا يمكن للمرء أن يقول أن بعض المكتبات تعمل بشكل أسرع من مكتبة أخرى ، ولكن يمكنك القيام ببعض العمليات التي تهمك. لقد اخترت مجموعة من العمليات المثيرة للاهتمام في طلبي ، وقمت بعمل مقياس وحصلت على مثل هذه النتائج.



اتضح أن وسادة - SIMD في هذه المجموعة تعمل بشكل أسرع مرتين من الوسادة. في النهاية هو Wand (أذكر أن هذا هو ImageMagick).

لكني كنت مهتمًا بشيء آخر - لماذا تكون OpenCV و VIPS ضعيفة جدًا في النتائج ، لأن هذه مكتبات تم تصميمها أيضًا بهدف الأداء؟ اتضح أنه في حالة OpenCV ، تم تجميع تجميع OpenCV الثنائي الذي تم تثبيته باستخدام النقطة باستخدام برنامج ترميز JPEG بطيء (تم إخطار مؤلف التجميع ، تم حل هذه المشكلة بالفعل لعام 2018). تم تصميمه باستخدام libjpeg ، في حين أن معظم الأنظمة ، على الأقل تعتمد على ديبيان ، تستخدم libjpeg-turbo ، وهو أسرع عدة مرات. إذا قمت بإنشاء OpenCV من المصدر بنفسك ، فسيكون الأداء أكبر.

في حالة VIPS ، يختلف الوضع. اتصلت بمؤلف VIPS ، وأظهرت له هذا المعيار ، وكنا نراسله لفترة طويلة ومثمرة. بعد ذلك ، وجد مؤلف VIPS عدة أماكن في VIPS نفسها ، حيث لم يكن التنفيذ على الطريق الأمثل ، وقام بتصحيحها.

هذا ما سيحدث للأداء إذا قمت بإنشاء OpenCV من مصادر الإصدار الحالي ، و VIPS من الرئيسي ، الموجود بالفعل.


حتى إذا وجدت نوعًا من المعايير ، فليس من الصحيح أن كل شيء سيعمل بهذه السرعة تمامًا على جهازك.

مجموعة من المعايير


يمكن العثور على جميع المعايير التي تحدثت عنها في صفحة النتائج . هذا مشروع صغير منفصل حيث أكتب المعايير التي أحتاجها بنفسي لتطوير وسادة- SIMD وتشغيلها ونشر النتائج.

يحتوي GitHub على مشروع بأطر اختبار حيث يمكن للجميع تقديم معاييرهم الخاصة أو إصلاح المعايير الموجودة.

العمل الموازي


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

مقاييس الأداء


من حيث الأداء ، نحن مهتمون بمعلمتين:

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

استنادًا إلى هذين المقياسين ، نعتبر طرقًا مختلفة للعملية المتوازية.

طرق العمل الموازية


1. على مستوى التطبيق ، عندما تقرر على مستوى التطبيق أن العمليات تتم في سلاسل عمليات مختلفة. في الوقت نفسه ، لا يتغير وقت التنفيذ الفعلي لعملية واحدة ، لأنه كما كان من قبل ، يشارك جوهر واحد في تسلسل واحد من العمليات. ينمو إنتاج النظام بما يتناسب مع عدد النوى ، وهذا جيد جدًا.

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

3. على مستوى أوامر وبيانات المعالج . نقوم بإعداد البيانات بطريقة خاصة واستخدام أوامر خاصة لجعل المعالج يعمل معهم بشكل أسرع. هذا هو نهج SIMD ، الذي يستخدم في الواقع في Pillow-SIMD. وقت التشغيل في الوقت الحقيقي آخذ في التناقص ، والإنتاجية تزداد - وهذا خيار مربح للجانبين .

كيفية الجمع بين العمل الموازي


إذا أردنا الجمع بين العمل الموازي بطريقة أو بأخرى ، فإن SIMD يعمل بشكل جيد مع التوازي داخل العملية ، وتعمل SIMD بشكل جيد مع التوازي داخل التطبيق.

لكن التوازي داخل التطبيق وداخل العملية غير متوافق مع بعضها البعض. إذا حاولت القيام بذلك ، فستحصل على سلبيات من كلا النهجين. سيكون الوقت الفعلي للعملية هو نفسه كما كان في قلب واحد ، وسيزيد إنتاجية النظام ، ولكن ليس بشكل خطي فيما يتعلق بعدد النوى.

تعدد


إذا كنا نتحدث عن سلاسل الرسائل ، فنحن جميعًا نكتب بلغة Python ونعلم أن لديها GIL يمنع تشغيل خيطين في نفس الوقت. Python هي لغة مفردة بدقة.

بالطبع ، هذا غير صحيح ، لأن GIL يمنع بالفعل تنفيذ موضوعين في Python ، وإذا كان الرمز مكتوبًا بلغة أخرى ولا يستخدم الهياكل الداخلية لـ Python أثناء تشغيله ، يمكن لهذا الرمز تحرير GIL وبالتالي تحرير المترجم لمهام أخرى.

تطلق العديد من مكتبات الرسومات GIL أثناء عملها ، بما في ذلك وسادة ، OpenCV ، pyvips ، Wand. لا يتم تحرير pgmagick واحد فقط. أي أنه يمكنك إنشاء سلاسل رسائل بأمان لإجراء بعض العمليات ، وسيعمل هذا بالتوازي مع باقي التعليمات البرمجية.

لكن السؤال الذي يطرح نفسه: كم عدد الخيوط لخلق؟

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

القاعدة ن +1

للعمل الإنتاجي ، لا تحتاج إلى إنشاء أكثر من N + 1 عاملاً ، حيث N هو عدد النوى أو خيوط المعالج على الجهاز ، والعامل هو العملية أو الخيط المتضمن في المعالجة.

من الأفضل استخدام العمليات ، لأنه حتى داخل نفس المترجم هناك اختناقات ونفقات عامة.

على سبيل المثال ، في تطبيقنا ، يتم استخدام N + 1 مثيل Tornado ، ويتم تنفيذ التوازن بينهما بواسطة ngnix. إذا تم ذكر تورنادو ، فلنتحدث عن العملية غير المتزامنة.

عملية غير متزامنة


الوقت الذي تقوم فيه مكتبة الرسومات بعمل مفيد بالفعل - معالجة الصور - يمكن ويجب استخدامه للإدخال / الإخراج ، إذا كان لديك في التطبيق. الأطر غير المتزامنة وثيقة الصلة هنا.

ولكن هناك مشكلة - عندما نسمي نوعًا من المعالجة ، يطلق عليها بشكل متزامن. حتى إذا قامت المكتبة بإصدار GIL في تلك اللحظة ، فإن حلقة الأحداث ما زالت محظورة.

 @gen.coroutine def get(self, *args, **kwargs): im = process_image(...) ... 

لحسن الحظ ، من السهل جدًا حل هذه المشكلة عن طريق إنشاء ThreadPoolExecutor بمؤشر ترابط واحد تبدأ فيه معالجة الصور. هذه المكالمة تحدث بالفعل بشكل غير متزامن.

 @run_on_executor(executor=ThreadPoolExecutor(1)) def process_image(self, ... @gen.coroutine def get(self, *args, **kwargs): im = yield process_image(...) ... 

في الأساس ، يتم إنشاء قائمة انتظار مع عامل واحد هنا لإجراء عمليات رسومية ، ولا يتم حظر حلقة الأحداث ويتم تشغيلها بهدوء بالتوازي في سلسلة رسائل أخرى.

الإدخال / الإخراج


موضوع آخر أود أن أتطرق إليه في مناقشة العمليات الرسومية هو الإدخال / الإخراج. والحقيقة هي أننا نادرًا ما ننشئ أي نوع من الصور باستخدام مكتبة رسومات. في أغلب الأحيان ، نفتح الصور التي وصلت إلينا من المستخدمين في شكل ملفات مشفرة (JPEG ، PNG ، BMP ، TIFF ، إلخ).

وفقًا لذلك ، يجب أن تحتوي مكتبة الرسومات لإنشاء تطبيق جيد على بعض الأشياء الجيدة للإدخال / الإخراج من الملفات.

كسول التحميل


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

 >>> from PIL import Image >>> %time im = Image.open() Wall time: 1.2 ms >>> im.mode, im.size ('RGB', (2152, 1345)) 

إذا قررت ما يجب فعله بعد ذلك ، فعندئذٍ باستخدام الدعوة الصريحة أو الضمنية للتحميل ، يتم فك تشفير هذه الصورة. بالفعل في هذه اللحظة يتم تخصيص المقدار الضروري من الذاكرة.

 >>> from PIL import Image >>> %time im = Image.open() Wall time: 1.2 ms >>> im.mode, im.size ('RGB', (2152, 1345)) >>> %time im.load() Wall time: 73.6 ms 

وضع الصورة المكسور


الكعكة الثانية المطلوبة عند العمل مع محتوى من إنشاء المستخدم هي وضع الصورة المقطوعة. غالبًا ما تحتوي الملفات التي نتلقاها من المستخدمين على بعض التناقضات في التنسيق الذي تم ترميزه به.

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

 from PIL import Image Image.open('trucated.jpg').save('trucated.out.jpg') IOError: image file is truncated (143 bytes not processed) 

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

 from PIL import Image, ImageFile ImageFile.LOAD_TRUNCATED_IMAGES = True Image.open('trucated.jpg').save('trucated.out.jpg') 



حتى الصورة التي تم اقتصاصها لا تزال أفضل من لا شيء - مجرد صفحة بها خطأ.

جدول الملخص




في الجدول أعلاه ، جمعت كل ما يتعلق بالإدخال / الإخراج في المكتبات التي أتحدث عنها. على وجه الخصوص ، أحصيت عدد برامج الترميز بتنسيقات مختلفة موجودة في المكتبات. اتضح أنه في OpenCV هم الأقل ، في ImageMagick - الأكثر. يبدو أنه في ImageMagick يمكنك فتح أي صورة تصادفها على الإطلاق. يحتوي VIPS على 12 برنامج ترميز أصلي ، ولكن يمكن لـ VIPS استخدام ImageMagick كوسيط. لم أختبر كيف يعمل هذا ، آمل أن يكون سلسًا.

الوسادة بها 17 كودك. هذه هي المكتبة الوحيدة التي لا يوجد فيها EXIF ​​تدوير تلقائي. ولكن الآن هذه مشكلة صغيرة ، لأنه يمكنك قراءة EXIF ​​بنفسك وتدوير الصورة وفقًا لها. هذا سؤال عن مقتطف صغير ، وهو سهل البحث في google ويستغرق 20 سطرًا كحد أقصى.

ميزات OpenCV


إذا نظرت إلى هذا الجدول بعناية ، يمكنك أن ترى ذلك في OpenCV ، في الواقع ، ليس كل شيء جيد جدًا مع الإدخال / الإخراج. يحتوي على أقل عدد من برامج الترميز ، ولا يوجد تحميل بطيء ، ولا يمكنك قراءة EXIF ​​وملف تعريف الألوان.

لكن هذا ليس كل شيء في الواقع ، لدى OpenCV المزيد من الميزات. عندما نقوم ببساطة بفتح صورة ، يقوم cv2.imread(filename) بتدوير ملفات JPEG وفقًا لـ EXIF ​​(انظر الجدول) ، ولكنه يتجاهل قناة ألفا لملفات PNG - وهو سلوك غريب نوعًا ما!

لحسن الحظ ، لدى OpenCV علامة: cv2.imread(filename, flags=cv2.IMREAD_UNCHANGED) .

إذا حددت علامة IMREAD_UNCHANGED ، فإن OpenCV يترك قناة ألفا لملفات PNG ، لكنه يتوقف عن تحويل ملفات JPEG وفقًا لـ EXIF. أي أن نفس العلم يؤثر على خاصيتين مختلفتين تمامًا. كما يتبين من الجدول ، ليس لدى OpenCV القدرة على قراءة EXIF ​​، وتبين أنه في حالة هذه العلامة ، من المستحيل تدوير JPEG على الإطلاق.

ماذا لو كنت لا تعرف مقدمًا ما هو تنسيق صورتك وتحتاج إلى قناة ألفا لـ PNG والتدوير التلقائي لـ JPEG؟ لا يوجد شيء للقيام به - لا يعمل OpenCV على هذا النحو.

السبب وراء وجود OpenCV لهذه المشاكل يكمن في اسم هذه المكتبة. لديها الكثير من الوظائف للرؤية الحاسوبية وتحليل الصور. في الواقع ، تم تصميم OpenCV للعمل مع مصادر تم التحقق منها. هذه ، على سبيل المثال ، كاميرا مراقبة خارجية تزيل الصور مرة واحدة في الثانية وتقوم بذلك لمدة 5 سنوات بنفس التنسيق ونفس الدقة. ليست هناك حاجة للتنوع في مشكلة الإدخال / الإخراج.

الأشخاص الذين يحتاجون إلى وظيفة OpenCV لا يحتاجون حقًا إلى وظائف محتوى المستخدم.

ولكن ماذا لو كان تطبيقك لا يزال بحاجة إلى وظائف للعمل مع محتوى المستخدم ، وفي نفس الوقت أنت بحاجة إلى كل قوة OpenCV للمعالجة والإحصائيات؟



الحل هو الجمع بين المكتبات. والحقيقة هي أن OpenCV مبني على أساس numpy ، ولوسدة كل الوسائل لتصدير الصور من الوسادة إلى مصفوفة numpy. أي أننا نقوم بتصدير المصفوفة numpy ، ويمكن لـ OpenCV الاستمرار في العمل مع هذه الصورة ، كما هو الحال مع الصورة الخاصة بها. يتم ذلك بسهولة:

 import numpy from PIL import Image ... pillow_image = Image.open(filename) cv_image = numpy.array(pillow_image) 

علاوة على ذلك ، عندما نقوم بعمل السحر باستخدام OpenCV (المعالجة) ، فإننا نسمي طريقة وسادة أخرى ونستورد الصورة من OpenCV مرة أخرى إلى تنسيق الوسادة. وفقًا لذلك ، يمكن استخدام I / O مرة أخرى.

 import numpy from PIL import Image ... pillow_image = Image.fromarray(cv_image, "RGB") pillow_image.save(filename) 

وبالتالي ، اتضح أننا نستخدم الإدخال / الإخراج من الوسادة ، والمعالجة من OpenCV ، أي أننا نأخذ أفضل ما في العالمين.

آمل أن يساعدك هذا في إنشاء تطبيق رسومات محمل.

يمكنك تعلم بعض أسرار التطوير الأخرى في Python ، والتعلم من تجربة لا تقدر بثمن وأحيانًا غير متوقعة ، والأهم من ذلك ، يمكنك مناقشة مهامك قريبًا جدًا في Moscow Python Conf ++ . على سبيل المثال ، انتبه إلى هذه الأسماء والموضوعات في الجدول الزمني.

  • دونالد وايت مع قصة حول كيفية جعل الرياضيات أسرع 10 مرات باستخدام المكتبات والحيل والماكرة الشعبية ، والشفرة مفهومة ومدعومة.
  • يدور أندريه بوبوف حول جمع كمية كبيرة من البيانات وتحليلها بحثًا عن التهديدات.
  • سيخبرك إفرايم ماتوسيان في تقريره "Make Python fast again مرة أخرى" بكيفية زيادة أداء البرنامج الخفي الذي يعالج الرسائل من الناقل.

قائمة كاملة لما سيتم مناقشته يومي 22 و 23 أكتوبر هنا ، لديها الوقت للانضمام.

Source: https://habr.com/ru/post/ar425471/


All Articles