Computer Visionপড়তে ৯ মিনিট লাগবে

নাম্বারের আদলে ছবি (Images as Numbers)

সব ছবিই আসলে নাম্বারের এক বিশাল গ্রিড (grid)
scope:কোর কনসেপ্ট (মূল ধারণা)difficulty:বিগিনার (Beginner)

যতটা সম্ভব জুম (Zoom) করুন

আপনার ফোনের যেকোনো একটি ছবি খুলুন এবং যতটা সম্ভব জুম করা যায় ততটা করুন। করতে থাকুন। একসময় আপনি ছোট ছোট কিছু রঙিন চারকোনা ঘর বা বক্স দেখতে পাবেন — এগুলোকে পিক্সেল (pixels) বলা হয়। নিজের তোলা সেলফি থেকে শুরু করে স্যাটেলাইটের ছবি পর্যন্ত, যেকোনো ডিজিটাল ছবিই আসলে অসংখ্য পিক্সেলের সমন্বয়ে তৈরি একটি গ্রিড (grid)।

আর প্রতিটি পিক্সেল কী দিয়ে তৈরি জানেন? স্রেফ তিনটি নাম্বার (number): একটি লাল (Red) রঙের জন্য, একটি সবুজ (Green)-এর জন্য, আর অন্যটি নীল (Blue)-এর জন্য। রঙের মতো এগুলোকে একত্রে মেশালেই আপনি যেকোনো রঙ তৈরি করতে পারবেন।

  • [255, 0, 0] = গাঢ় লাল (pure red)
  • [0, 255, 0] = গাঢ় সবুজ (pure green)
  • [0, 0, 255] = গাঢ় নীল (pure blue)
  • [255, 255, 0] = হলুদ (লাল + সবুজ)
  • [255, 255, 255] = সাদা (সব রঙ মিলিয়ে)
  • [0, 0, 0] = কালো (কোনো আলোর উপস্থিতি নেই)

স্মার্টফোনে তোলা একটি সাধারণ ছবির সাইজ বা আকার সাধারণত 4000 x 3000 পিক্সেল হয়। অর্থাৎ, একটি ছবিতে ১ কোটি ২০ লাখ পিক্সেল থাকে এবং প্রতিটি পিক্সেলের আবার ৩টি করে কালার ভ্যালু থাকে — তার মানে একটিমাত্র ছবিকে বর্ণনা করতে ৩ কোটি ৬০ লাখ নাম্বারের প্রয়োজন হয়।

এআই-এর জন্য এটি কেন এত জরুরি

যখন একটি ছবি কেবল কিছু নাম্বারের গ্রিডে পরিণত হয়, তখন কম্পিউটার খুব সহজেই তার ওপর গাণিতিক হিসাব-নিকাশ করতে পারে। আর সত্যি বলতে, নিউরাল নেটওয়ার্ক (neural networks)-এর মূল কাজই হচ্ছে এই অঙ্ক বা হিসাব-নিকাশ করা। আপনি কি ছবির উজ্জ্বলতা (Brightness) বদলাতে চান? নাম্বারগুলোর গড় করুন। প্রান্ত বা এজ (Edges) বের করতে চান? আশপাশের পিক্সেলগুলো বিয়োগ করুন। ব্লার (Blur) বা ঝাপসা করতে চান? আশেপাশের এলাকার নাম্বারগুলোর গড় করুন। অর্থাৎ, ছবির ওপর করা যেকোনো পরিবর্তনই আসলে একটি গ্রিডের ওপর করা সাধারণ পাটিগণিতের হিসাব মাত্র।

গ্রেস্কেল (Grayscale): আরও সহজ একটি রূপ

রঙিন ছবিতে মোট ৩টি চ্যানেল (Channel) থাকে (R, G, B)। কিন্তু গ্রেস্কেল (Grayscale) বা সাদাকালো ছবিতে কেবল ১টি চ্যানেল থাকে — যা আদতে 0 (কালো) থেকে 255 (সাদা) পর্যন্ত একটি উজ্জ্বলতার মান বা ব্রাইটনেস ভ্যালু ধারণ করে। এগুলো নিয়ে কাজ করা অনেক সহজ, আর এ কারণেই কম্পিউটার ভিশন (computer vision)-এর অনেক ক্লাসিক বা পুরোনো অ্যালগরিদমগুলো এই গ্রেস্কেল ছবি দিয়েই কাজ শুরু করে।

ম্যাট্রিক্স বা টেন্সর (Tensor) হিসেবে ছবি

একটি কম্পিউটারের কাছে, কোনো ছবি হলো একটি থ্রিডি অ্যারে বা 3D array (যাকে টেন্সর বা tensor-ও বলা হয়):

  • উচ্চতা (Height) x প্রস্থ (Width) x চ্যানেল বা রঙের স্তর (Channels)
  • একটি 28x28 গ্রেস্কেল ছবির শেইপ বা আকার দাঁড়ায় → (28, 28, 1)
  • একটি 1920x1080 রঙিন ছবির শেইপ বা আকার দাঁড়ায় → (1080, 1920, 3)

কম্পিউটার ভিশনের ক্ষেত্রে এটিই হলো সবচেয়ে মৌলিক ডেটা স্ট্রাকচার (data structure)। আপনি যখন কোনো নিউরাল নেটওয়ার্কে একটি ছবি ইনপুট হিসেবে দেন, তখন আপনি মূলত তাকে নাম্বারের একটি টেন্সরই (tensor) দিচ্ছেন।

রেজোলিউশন (Resolution) এবং ইনফরমেশন

যত বেশি পিক্সেল = তত বেশি ডিটেইল = তত বেশি নাম্বার। একটি 4K ছবিতে (3840x2160) প্রায় ৮৩ লাখ পিক্সেল থাকে — যেখানে এআই (AI)-এর বিভিন্ন পরীক্ষায় ব্যবহৃত খুব ছোট একটি 28x28 থাম্বনেইল বা ছবিতে মাত্র ৭৮৪টি পিক্সেল থাকে। বিখ্যাত 'MNIST' হাতে লেখা নাম্বারের ডেটাসেটটিতেও এমন 28x28-এর গ্রেস্কেল ছবি ব্যবহার করা হয়েছে। এটি খুবই সাধারণ, কিন্তু একটি নিউরাল নেটওয়ার্কের জন্য ৯৯% এরও বেশি নির্ভুলতার সাথে নাম্বার চেনার জন্য এটিই যথেষ্ট।

নাম্বার হিসেবে ছবি নিয়ে কাজ করা

import numpy as np
# একটি খুব ছোট 4x4 সাইজের RGB ছবি তৈরি
image = np.zeros((4, 4, 3), dtype=np.uint8)
# কিছু পিক্সেলে রঙ করা
image[0, 0] = [255, 0, 0] # লাল (Red)
image[0, 1] = [0, 255, 0] # সবুজ (Green)
image[0, 2] = [0, 0, 255] # নীল (Blue)
image[0, 3] = [255, 255, 0] # হলুদ (Yellow)
image[1, :] = [128, 128, 128] # ধূসর বা গ্রে (Gray) রঙের সারি
image[2, :] = [255, 255, 255] # সাদা রঙের সারি
image[3, :] = [0, 0, 0] # কালো রঙের সারি
print(f"ছবির শেইপ: {image.shape} (height, width, RGB)")
print(f"মোট নাম্বার: {image.size}")
print(f"\nএকদম ওপরের বাঁ দিকের পিক্সেল (লাল): {image[0, 0]}")
print(f"ধূসর বা গ্রে (Gray) পিক্সেল: {image[1, 0]}")
# সাধারণ ফর্মুলা ব্যবহার করে একে গ্রেস্কেলে (grayscale) রূপান্তর করা
def to_grayscale(img):
return (0.299 * img[:,:,0] + 0.587 * img[:,:,1] + 0.114 * img[:,:,2]).astype(np.uint8)
gray = to_grayscale(image)
print(f"\nগ্রেস্কেল ছবির শেইপ: {gray.shape}")
print(f"গ্রেস্কেল ভ্যালু:\n{gray}")
# এজ বা সীমানা খুঁজে বের করা (আশেপাশের পিক্সেলের মাঝে উজ্জ্বলতার পার্থক্য বের করা)
print(f"\nসারি বা রো (row)-গুলোর মাঝে উজ্জ্বলতার পার্থক্য:")
for i in range(len(gray) - 1):
diff = gray[i+1].astype(int) - gray[i].astype(int)
print(f" রো বা সারি {i}{i+1}: {diff}")
Output
ছবির শেইপ: (4, 4, 3)  (height, width, RGB)
মোট নাম্বার: 48

একদম ওপরের বাঁ দিকের পিক্সেল (লাল): [255   0   0]
ধূসর বা গ্রে (Gray) পিক্সেল: [128 128 128]

গ্রেস্কেল ছবির শেইপ: (4, 4)
গ্রেস্কেল ভ্যালু:
[[ 76 149  29 226]
 [128 128 128 128]
 [255 255 255 255]
 [  0   0   0   0]]

সারি বা রো (row)-গুলোর মাঝে উজ্জ্বলতার পার্থক্য:
  রো বা সারি 0→1: [  52  -21   99  -98]
  রো বা সারি 1→2: [127 127 127 127]
  রো বা সারি 2→3: [-255 -255 -255 -255]
Note: কেন 0 থেকে 255? রঙের প্রতিটি চ্যানেলকে ১ বাইট (বা ৮ বিট)-এ জমা রাখা হয়, যা 0 থেকে 255 পর্যন্ত মোট ২৫৬টি মান (value) ধরে রাখতে পারে। অর্থাৎ প্রতি চ্যানেলে আপনি ২৫৬টি স্তরের (level) রঙ পাচ্ছেন। তাহলে তিন রঙের মিশ্রণে মোট 256 x 256 x 256 = ১ কোটি ৬৭ লাখ রঙের বৈচিত্র্য পাওয়া সম্ভব। যা আসলে প্রয়োজনের তুলনায় অনেক বেশি — কারণ মানুষের চোখ প্রকৃতিতে সবমিলিয়ে প্রায় ১ কোটি কালার বা রঙের মধ্যে পার্থক্য করতে পারে।

পিক্সেল থেকে আসল বোঝাপড়া

একটি মাত্র পিক্সেল দেখে আসলে কিছুই বোঝা যায় না। এটি কেবল একটি রঙ। কিন্তু অনেকগুলো পিক্সেল একসাথে মিলে একটি প্রান্ত বা এজ (edges) তৈরি করে। আবার অনেকগুলো এজ মিলে তৈরি হয় আকৃতি বা শেইপ (shapes)। আর সেই শেইপগুলো মিলে তৈরি হয় একটি পূর্ণাঙ্গ বস্তু (objects)। এই যে ছোট থেকে বড় হওয়ার ধারাবাহিকতা — প্রথমে নাম্বার, তারপর পিক্সেল, এজ, শেইপ, বস্তু এবং সবশেষে একটি পুরো দৃশ্য — নিউরাল নেটওয়ার্কগুলো ঠিক এভাবেই স্তরে স্তরে বা লেয়ার ধরে চারপাশের জগতকে চিনতে শেখে।

"নাম্বারের গ্রিড" থেকে "এটি সমুদ্রসৈকতে থাকা একটি গোল্ডেন রিট্রিভার (golden retriever) কুকুরের ছবি"—কম্পিউটার ভিশনের এই লম্বা যাত্রার শুরুটা হয় ওই ছোট্ট আর সাদামাটা পিক্সেলের হাত ধরেই।

ছোট কুইজ

একটি রঙিন ছবির প্রতিটি পিক্সেলে কয়টি নাম্বার থাকে?

পড়া চালিয়ে যান