পাঠ ০৩ · ৩৫-এর মধ্যে · মডিউল ১
Home / AI Courses / Computer Vision / OpenCV I/O

OpenCV-এ ছবি পড়া ও লেখা

OpenCV image I/O — imread, imwrite, imshow
৬ মিনিট পড়া শুরু · Beginner OpenCV কোডসহ

এই পাঠে যা শিখবেন

  • OpenCV install ও basic flow
  • imread flag — color, grayscale, unchanged
  • JPG, PNG, BMP, WebP ফরম্যাট কোনটা কখন
  • Resize, crop ও basic save

১ · OpenCV কী?

OpenCVOpenCVOpen Source Computer Vision Library — ১৯৯৯-এ Intel-এ শুরু। C++ মূল, Python, Java, JavaScript binding। ২৫০০+ algorithm, প্রায় সব CV কাজের de-facto library। CV-র সবচেয়ে জনপ্রিয় library। ছবি পড়া-লেখা থেকে শুরু করে edge detection, face recognition — সবই আছে। Install:

pip install opencv-python
কেন্দ্রীয় ধারণা

OpenCV-তে ছবি = NumPy ndarray। তাই NumPy যা কিছু পারে — slicing, broadcasting, vectorize — সবই OpenCV ছবিতে কাজ করে।

২ · imread — file থেকে tensor

cv2.imread(path, flag) — file load করে NumPy array দেয়। সফল না হলে None।

  • cv2.IMREAD_COLOR (default, 1) — BGR 3-channel। Alpha (transparency) discard।
  • cv2.IMREAD_GRAYSCALE (0) — সরাসরি 1-channel grayscale। দ্রুত।
  • cv2.IMREAD_UNCHANGED (-1) — সব channel + alpha (PNG-এ 4-channel)।
imread = file-এর pixel গুলো decoder দিয়ে decompress করে memory-তে আনা। JPEG-এর Huffman+DCT, PNG-এর zlib — সব internally।

৩ · imwrite — save

cv2.imwrite('out.jpg', img, [params])। Extension থেকে format ঠিক হয় — .jpg, .png, .bmp, .webp।

Quality control:

  • JPEG: [cv2.IMWRITE_JPEG_QUALITY, 95] (0-100)
  • PNG: [cv2.IMWRITE_PNG_COMPRESSION, 3] (0-9, higher=smaller-but-slower)
  • WebP: [cv2.IMWRITE_WEBP_QUALITY, 85]

৪ · ফরম্যাট তুলনা

ফরম্যাটCompressionAlphaBest for
JPEGLossyনাphoto, web
PNGLosslessহ্যাঁgraphic, mask
BMPনেইনাdebug, raw
WebPদু'টোইহ্যাঁmodern web
TIFFLosslessহ্যাঁscientific, medical

৫ · imshow — display

Desktop Python: cv2.imshow('window', img) + cv2.waitKey(0)। Notebook/Colab: matplotlib (plt.imshow) ব্যবহার করুন — কারণ headless environment-এ imshow error।

OpenCV I/O — file ও tensor-এর সাঁকো 📁 File photo.jpg / .png compressed bytes 🔢 NumPy ndarray (H, W, 3) BGR 🖥️ Display imshow / plt screen pixel imread() decode imshow() render ⚙️ Process resize, filter, etc. imwrite() পুরো pipeline NumPy operations
imread → ndarray → process → imwrite/imshow। প্রতিটি step NumPy tensor-এ।

৬ · একটি ছবি load ও save

Python · OpenCV
import cv2
import numpy as np

# একটি synthetic ছবি বানাই (যাতে file লাগবে না)
img = np.zeros((200, 300, 3), dtype=np.uint8)
img[:100, :150] = [255, 100, 50]   # নীলাভ patch (BGR)
img[100:, 150:] = [50, 200, 100]   # সবুজ patch

# JPG-তে save (quality 90)
cv2.imwrite('demo.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])

# আবার load করি
loaded = cv2.imread('demo.jpg')
print("Shape:", loaded.shape)        # (200, 300, 3)
print("Dtype:", loaded.dtype)        # uint8
print("ক্ষতিগ্রস্ত মান (lossy):", np.abs(img.astype(int) - loaded.astype(int)).mean())

    
JPEG lossy — তাই save → load round-trip-এ pixel value সামান্য বদলায়। PNG দিয়ে save করলে exact match হতো।

৭ · Resize ও crop

Python · OpenCV
import cv2
import numpy as np

img = np.random.randint(0, 256, (480, 640, 3), dtype=np.uint8)
print("Original:", img.shape)

# Resize — (width, height) order!
small = cv2.resize(img, (224, 224))
print("Resized:", small.shape)        # (224, 224, 3)

# Aspect-ratio preserving resize
h, w = img.shape[:2]
target_w = 320
target_h = int(h * target_w / w)
fit = cv2.resize(img, (target_w, target_h))
print("Fit:", fit.shape)

# Crop — NumPy slicing
crop = img[100:300, 200:400]
print("Crop:", crop.shape)            # (200, 200, 3)

    
cv2.resize-এ size = (width, height) — NumPy shape-এর উল্টো। মনে রাখার একটি common pitfall।

৮ · Interpolation method

Resize-এ interpolation:

  • cv2.INTER_NEAREST — দ্রুত, blocky। mask resize-এ আদর্শ।
  • cv2.INTER_LINEAR (default) — bilinear, balanced।
  • cv2.INTER_CUBIC — bicubic, smoother (slow)।
  • cv2.INTER_AREA — shrinking-এ best, no aliasing।
  • cv2.INTER_LANCZOS4 — highest quality, slow।

৯ · Common pitfall

  • imread None: path ভুল বা format unsupported। always check।
  • BGR vs RGB: matplotlib-এ দেখাতে গেলে convert করুন।
  • Path with non-ASCII: কিছু OpenCV build-এ Bangla path fail করে — use absolute ASCII path।
  • EXIF rotation: imread auto-rotate করে না; PIL করে। ফলে phone photo upside down হতে পারে।
Production-এ imread-এর return None check করুন — file missing, permission denied, বা corrupt হলে নীরবে None আসে। assertion দিন।

ভাবনার প্রশ্ন

প্র ০১ একটি 5MB JPG ছবি load করে আবার 95% quality-তে save করলাম। ফাইল আকার ৫MB থেকে ৪MB হলো। আবার load-save করলে আরো ছোট হবে? কেন?

এটি generation loss — JPEG-এর কুখ্যাত সমস্যা। প্রতিটি save-এ ছবি কিছুটা ক্ষতিগ্রস্ত হয়।

কেন আকার বদলায়?

  • প্রথম save-এ original-এর কিছু high-frequency detail হারায় — DCT coefficient quantize হয়।
  • পরের save-এ ক্ষতি হওয়া ছবি input — কিছু detail আগেই নেই, তাই compress easier।
  • কয়েক generation পর — visible artifact (block, ringing, color fade)।

আকার আরও ছোট হয় কি?

  • প্রথম কয়েকবার সামান্য ছোট হবে।
  • পরে stable — কারণ "compressible" detail সব চলে গেছে।
  • কিন্তু visual quality ক্রমাগত খারাপ — ১০ generation-এ ছবি প্রায় unrecognizable।

প্রমাণ — ৫০০ time round-trip:

img = cv2.imread('orig.jpg')
for i in range(500):
    cv2.imwrite('temp.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])
    img = cv2.imread('temp.jpg')
# এখন img প্রায় cartoonish, washed-out

Practical implication:

  • Photo editing — PNG বা TIFF-এ intermediate save।
  • Final export-ই JPEG।
  • ML augmentation-এ JPEG re-save এড়ান — model JPEG artifact শিখে নিতে পারে।
  • Forensics — JPEG ghost detection generation analysis দিয়ে edit prove করে।

মূল উপলব্ধি: Lossy = irreversible। Source-of-truth কখনো overwrite করবেন না — backup archival format।

প্র ০২ OpenCV-তে ছবি resize করার সময় INTER_AREA কেন shrinking-এ best, কিন্তু enlarging-এ INTER_CUBIC? Aliasing কী ঘটছে?

এটি signal processing-এর একটি deep concept। Image resize = sampling + reconstruction।

Shrinking (downsample) — যা ঘটে:

  • 1000×1000 → 100×100 — প্রতি ১০ pixel-এ ১টি পিক্সেল-এ মেপ করতে হয়।
  • Aliasing: high-frequency detail (ছোট pattern) misrepresent — moiré pattern, jagged edge।
  • Nyquist-এর সূত্র: target frequency-র অন্তত দ্বিগুণ rate-এ sample চাই।

INTER_AREA-এর কৌশল:

  • প্রতিটি output pixel = source-এর corresponding rectangular area-এর গড়।
  • মূলত box filter — natural anti-aliasing।
  • Detail হারায় কিন্তু moiré আসে না।

Enlarging (upsample) — যা ঘটে:

  • 100×100 → 1000×1000 — মাঝখানে পিক্সেল "তৈরি" করতে হয়।
  • সমস্যা ভিন্ন — aliasing না, blurriness।

INTER_CUBIC vs INTER_LINEAR:

  • Linear: 4 neighbor-এর weighted avg। দ্রুত কিন্তু slightly blurry।
  • Cubic: 16 neighbor + cubic spline। edge-এ sharper, but slow ও কিছু overshoot (halo) হতে পারে।
  • Lanczos: 64+ neighbor, sinc-based। highest quality।

Mask/label resize:

  • Segmentation mask-এ INTER_NEAREST ব্যবহার — অন্যথা class label interpolate হয়ে invalid value।
  • e.g. class 1 ও 2-এর মাঝে 1.5 — যা কোনো class না।

মূল উপলব্ধি: Resize trivial না — sampling theory-র গভীরে। ML pipeline-এ wrong interpolation ৫% accuracy-র পার্থক্য আনতে পারে।

প্র ০৩ একটি ১০,০০০ ছবির dataset-এ training করতে গিয়ে I/O bottleneck — disk read slow। কী কী strategy আছে?

DL-এর সবচেয়ে underrated bottleneck — disk I/O। GPU 99% idle হয়ে বসে থাকে যখন data loader slow।

(১) Sequential read optimize:

  • HDF5/LMDB/TFRecord: ১০,০০০ ছোট file-এর বদলে এক বড় container। random seek 10x faster।
  • WebDataset: tar archive-এ shards — sequential streaming।

(২) Caching:

  • প্রথম epoch-এ disk read, RAM-এ cache। পরের epoch RAM থেকে।
  • Linux page cache automatic — যথেষ্ট RAM থাকলে।
  • Decoded ছবি cache — JPEG decode CPU-heavy।

(৩) Parallel loading:

  • PyTorch num_workers=8 — multi-process data load।
  • GPU train করার সময় CPU পরের batch prepare।
  • pin_memory=True — pinned memory → GPU transfer fast।

(৪) Storage choice:

  • HDD → SSD: 10× speedup typically।
  • SSD → NVMe: 3-5× more।
  • Network FS এড়ান training-এ — local copy।

(৫) On-the-fly decoding:

  • NVIDIA DALI: GPU-decoded JPEG — CPU bypass।
  • libjpeg-turbo: 2× faster than default libjpeg।
  • Pillow-SIMD: PIL-এর SIMD-optimized fork।

(৬) Data format:

  • Resize ১ বার — একবার preprocess, পুনরায় না।
  • JPEG quality 80 যথেষ্ট — file ছোট, decode fast।
  • Critical task: lossless WebP বা PNG।

Profiling rule: GPU utilization 70%-র নিচে হলে — I/O bottleneck। Optimize আগে measure।

মূল উপলব্ধি: "Bigger model" নয়, "faster pipeline" — অনেকসময় বড় গেইন। MLOps-এর core skill।

প্র ০৪ একটি phone থেকে নেওয়া ছবি OpenCV-তে load করলে দেখলেন — orientation rotated (পাশে শুয়ে আছে)। কী হচ্ছে, কীভাবে fix?

এটি EXIF orientation-এর problem। প্রায় সব phone camera ছবি তোলে landscape sensor-এ — orientation metadata-তে store।

EXIF কী?

  • Exchangeable Image File Format — JPEG-এর header-এ metadata।
  • Camera model, GPS, exposure, ও orientation tag (1-8)।
  • Tag 1 = normal, 6 = 90° CW rotate, 8 = 90° CCW, 3 = 180°।

সমস্যা OpenCV-তে:

  • OpenCV imread EXIF orientation respect করে না।
  • Pixel data যেমন sensor-এ ছিল তেমনই load হয় — phone-এর "vertical" ছবি horizontal।
  • Phone gallery, Pillow, browser auto-rotate দেখায় বলে user বোঝে না।

সমাধান (Pillow ব্যবহার):

from PIL import Image, ImageOps
import numpy as np
import cv2

pil_img = Image.open('phone_photo.jpg')
pil_img = ImageOps.exif_transpose(pil_img)  # auto rotate
img_rgb = np.array(pil_img)
img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)

সমাধান (manual EXIF read):

import piexif
exif = piexif.load('photo.jpg')
orientation = exif['0th'].get(piexif.ImageIFD.Orientation, 1)
# তারপর cv2.rotate() করে correct

OpenCV 4.7+: cv2.imread(path, cv2.IMREAD_IGNORE_ORIENTATION | cv2.IMREAD_COLOR) — opposite, EXIF apply default 4.7+ থেকে। Version-ভেদে behavior ভিন্ন!

Production checklist:

  • User upload-এ orientation normalize করুন server-side।
  • Database-এ storage-এর আগে EXIF strip + rotate apply।
  • ML inference-এ orientation না জানলে — rotation augmentation দিয়ে training।

মূল উপলব্ধি: Real-world image data noisy — metadata, encoding, orientation সবই inconsistent। CV engineer-এর কাজ pipeline-এ এই সব handle করা।

অনুশীলন

  1. Quality experiment: একটি ছবি quality 10, 50, 95-এ JPG save করুন। File size ও visual difference compare করুন।
    for q in [10, 50, 95]:
        cv2.imwrite(f'q{q}.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, q])
    import os
    for q in [10, 50, 95]:
        print(q, os.path.getsize(f'q{q}.jpg'), 'bytes')

    সাধারণত 95 → 50 → 10 আকার ১০x ছোট হয়, কিন্তু q=10 এ visible block artifact।

  2. Aspect-ratio safe resize: যেকোনো size-এর ছবিকে max dimension=512 রেখে resize করার একটি function লিখুন।
    def resize_max(img, max_dim=512):
        h, w = img.shape[:2]
        scale = max_dim / max(h, w)
        if scale >= 1: return img
        new_w, new_h = int(w*scale), int(h*scale)
        return cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
  3. ভাবুন: একটি AI medical app-এ DICOM (16-bit) X-ray image। JPEG-এ save করলে কী সমস্যা?

    JPEG 8-bit only — 16→8 bit conversion-এ subtle tissue contrast হারায়। Lossy compression cancer detect-এর critical region damage করতে পারে। Medical-এ DICOM বা lossless TIFF-ই standard।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

কোড রানার কাজ না করলে? ব্রাউজারে কাজ না করলে Google Colab ব্যবহার করুন — Google-এর ফ্রি অনলাইন Python পরিবেশ, শুধু Gmail অ্যাকাউন্ট লাগে।
পূর্ববর্তী পাঠ
পাঠ ০২ · Color spaces