مخطط ألوان 24 بت ومخطط رمادي 8 بت
أولاً ، دعونا نقدم صورة ملونة 24 بت. في صورة ملونة 24 بت ، يتم تمثيل كل بكسل بثلاثة بايت ، وعادة ما يتم التعبير عنها باسم RGB. بشكل عام ، يتم تخزين العديد من الصور الملونة على مدار 24 بت كصور 32 بت ، ويتم تخزين البايتات الزائدة لكل بكسل كقيمة alpha ، مما يدل على معلومات التأثير الخاص [1].
في نموذج RGB ، إذا كان r = g = b ، يمثل اللون لونًا رماديًا ، حيث تسمى قيمة r = g = b القيمة الرمادية. لذلك ، يتطلب كل بكسل من الصورة الرمادية بايت واحد فقط لتخزين قيمة الرمادي (المعروف أيضًا باسم قيمة الكثافة وقيمة السطوع) ، ومدى رمادي هو 0-255 [2]. هذا يمنحك صورة رمادية للصورة.
عدة طرق للترات الرمادية
1. طريقة المكون: استخدم أحد مكونات RGB الثلاثة كقيمة رمادية لخريطة الرمادي.
2. معظم طريقة القيمة: استخدم القيمة القصوى أو الحد الأدنى لمكونات RGB الثلاثة كقيمة رمادية لخريطة الرمادي.
3. الطريقة المتوسطة: استخدم متوسط القيمة لمكونات RGB الثلاثة كقيمة رمادية للرسم البياني الرمادي.
4. طريقة الترجيح: نظرًا لحساسية اللون المختلفة للعينان البشريون ، يمكن للمتوسط المرجح لـ RGB ثلاثة مكونات يمكن أن تحصل على صورة رمادية أكثر منطقية. الوضع العام كما يلي: y = 0.30r + 0.59g + 0.11b.
[ملاحظة] تأخذ طريقة الترجمة في الواقع قيمة السطوع للصورة كقيمة رمادية لحسابها ، وتستخدم نموذج YUV. في [3] ، ستجد أن المؤلف يستخدم y = 0.21 * r + 0.71 * g + 0.07 * b لحساب قيمة الرمادي (من الواضح أن مجموع الأوزان الثلاثة لا يساوي 1 ، والذي قد يكون خطأ المؤلف؟). في الواقع ، يجب أن يكون هذا الاختلاف مرتبطًا بما إذا كان يتم استخدام تصحيح جاما [1].
طريقة لتنفيذ رمادي في جافا
إذا بحثت عن "Java Passions Grayscale" ، فإن معظمها طريقة واحدة (رمز):
public void grayimage () يلقي ioException {file = file new (system.getProperty ("user.dir")+"/test.jpg") ؛ صورة bufferedImage = imageio.read (ملف) ؛ int width = image.getWidth () ؛ ارتفاع int = image.getheight () ؛ BufferedImage GrayImage = جديد bufferedImage (العرض ، الارتفاع ، bufferedImage.type_byte_gray) ؛ لـ (int i = 0 ؛ i <width ؛ i ++) {for (int j = 0 ؛ j <height ؛ j ++) {int rgb = image.getrgb (i ، j) ؛ GrayImage.setrgb (I ، J ، RGB) ؛ }} file newFile = new file (system.getProperty ("user.dir")+"/method1.jpg") ؛ imageio.write (GrayImage ، "JPG" ، newFile) ؛ }الصورة الأصلية لـ test.jpg هي:
مخطط الرمادي الذي تم الحصول عليه باستخدام الطريقة أعلاه:
يبدو أن رؤية هذه الصورة الرمادية ممكنة ، ولكن إذا استخدمنا opencv لتحقيق رمادي أو استخدام PIL (Python) ، فستجد أن التأثيرات تختلف بشكل كبير:
img = cv2.imread ('test.jpg' ، cv2.imread_color) gray = cv2.cvtcolor (img ، cv2.color_bgr2gray) يمكن أن نرى بوضوح أن الرسم البياني الرمادي الذي تم الحصول عليه باستخدام opencv (وينطبق نفسه على PIL) أفضل بكثير من الطريقة التي تم الحصول عليها بواسطة طريقة Java أعلاه ، ويمكن رؤية العديد من التفاصيل. هذا يدل على أن هذه الطريقة الشائعة على الإنترنت كانت تواجه دائمًا بعض المشكلات ، ولكن تم تجاهلها.
كيفية تحقيق رمادي في OpenCV
إذا كنت قد قرأت الكتب أو الرموز المتعلقة بـ opencv ، فربما يمكنك معرفة أن opencv Grayscale يستخدم طريقة الترجيح. والسبب هو أن ذلك يرجع تقريبًا لأننا لا نعرف لماذا تكون صور opencv Grayscale جيدة جدًا. هل هناك أي تفاصيل معالجة أخرى نتجاهلها؟
التحقق من تخميننا بسيط للغاية. ما عليك سوى التحقق من التغييرات قبل وبعد قيمة البيكسل. يمكنك اختباره على النحو التالي:
img = cv2.imread ('test.jpg' ، cv2.imread_color) h ، w = img.shape [: 2] gray = cv2.cvtcolor (img ، cv2.color_bgr2gray) for j in range (w). IMG [H-1] [W-1] [0: 3]من الصعب علينا أن نحكم على العديد من البكسلات أدناه ، لكننا بحاجة فقط إلى الانتباه إلى آخر نقطة بكسل ويمكننا العثور على أدلة: قيمة RGB لآخر نقطة بكسل في الصورة الأصلية هي 44 ، 67 ، 89 ، والقيمة بعد الرمادي هي 71. إنها تناسب قيمة Grayscale المحسوبة حسب طريقة الوزن. إذا قمت بفحص قيم البكسل للصورة التي تم تجويفها في Java من قبل ، فستجد أنه ليس فقط قيم البكسل لا تفي بهذه الصيغة ، بل إنها بعيدة عن بعضها البعض.
في هذه المرحلة ، نعتقد أن OpenCV (بما في ذلك PIL) هو تطبيق رمادي باستخدام طريقة الترجيح.
تقوم Java بتنفيذ طريقة رمادية مرجحة
إذا لم تنجح هذه الطريقة الشائعة على الإنترنت ، فكيف يجب أن نستخدم Java لتحقيق رمادي؟ في الواقع ، [3] حقق بنجاح (طرق متعددة) رمادية (لا يزال الأصدقاء الأجانب قويين للغاية في التكنولوجيا) ، ويتم استخراج الكود الضروري فقط هنا:
private static int colortorgb (int alpha ، int red ، int green ، int blue) {int newPixel = 0 ؛ NewPixel += ألفا ؛ newPixel = newPixel << 8 ؛ NewPixel += أحمر ؛ newPixel = newPixel << 8 ؛ NewPixel += الأخضر ؛ newPixel = newPixel << 8 ؛ NewPixel += Blue ؛ إرجاع Newpixel ؛ } يبرز الفراغ الثابت العام (سلسلة [] args) ioException {bufferedImage bufferedImage = imageio.read (ملف جديد (System.getProperty ("user.dir" + "/test.jpg")) BufferedImage.getType () ؛ 0xff ؛ File (System.getProperty ("user.dir") + "/ok.jpg") ؛ سيقوم الرمز أعلاه بطباعة قيم البكسل على نطاق رمادي. إذا قارنتها برمز Python أعلاه ، فستجد أن قيم البكسل مقابلة تمامًا. تعالج طريقة colorToRGB مخطط اللون بالضبط 4 بايت ، أحدها هو معلمة alpha (كما ذكر سابقًا). الشكل التالي هو الصورة الرمادية لهذا الرمز:
لطرق أخرى ، يمكن الحصول على نفس الشيء بدوره.
لخص
السبب في هذه المقالة هو استخدام Java لتنفيذ العديد من عمليات الرمادي واستخدام OpenCV للتحقق من صواب التحويل أو خطأ. ومع ذلك ، تم العثور على بعض المشكلات في الاختبارات الفعلية (هناك اختلافات في الصور المحولة ، وكيفية إنشاء صور رمادية بناءً على القيم الرمادية بعد التحويل) ، وتم إجراء بعض الأفكار والتحققات حول هذا الموضوع. تجدر الإشارة هنا إلى أن بعض المقالات على الإنترنت قد قامت بتفكير مزيد من التفكير (ويتم نسخ العديد منها ، وخاصة المقالات المحلية) ، ولهذه القضايا العملية ، يعد التنفيذ العملي والتحقق طريقة مهمة للغاية. آمل أن يكون محتوى هذه المقالة مفيدًا للجميع. إذا كان لديك أي أسئلة ، فيرجى ترك رسالة لمناقشة.