محاسبه SNR و PSNR تصویر در پایتون

اگر تا اینجا اومدید قطعا می‌دونید SNR و PSNR تصویر چی هستن و به چه دردی می‌خورن! پس چی بهتر از اینکه بریم و اون‌ها رو در پایتون محاسبه کنیم؟!

اول تفاوت SNR و PSNR رو بدونیم:

Signal to Noise Ratio:
It shows the relationship between the real image and estimated image. This ratio indicates how strong the noise corrupted the original image.

Peak Signal to Noise Ratio:
In PSNR we are interested in signal peak. This is more content specific than pure SNR.Here we say how high intensity regions of the image come through the noise and paying much less attention to low intensity regions.

فرمول‌های SNR و PSNR رو با هم ببینیم:

حالا که بخش ریاضی کار رو می‌دونیم کد نویسی ساده و شیرین میشه.

فقط یک نکته‌ی خیلی مهم داریم: وقتی تصویر رو در OpenCV می‌خونیم به شکل پیشفرض فرمت داده Uint-8 هست، یعنی اعداد در بازه‌ی ۰ تا ۲۵۵ قرار دارن.

توی فرمول‌های بالا ما باید مقدار پیکسل‌ها رو به توان ۲ برسونیم پس اگر فرمت داده رو تغییر ندیم، اووپس! محاسباتمون اشتباه می‌شن:)

تابع محاسبه SNR:

def snr(img, ref):
    #convert imput images to float64
    _img = np.float64(np.copy(img))
    _ref = np.float64(np.copy(ref))
    
    sum_img = np.sum((_img)**2)
    sum_noise = np.sum((_img - _ref)**2)
    if sum_noise == 0:
        return 100
    return 10 * math.log10(sum_img / sum_noise)

تابع محاسبه‌ی PSNR:

def psnr(img, ref):
    #convert imput images to float64
    _img = np.float64(np.copy(img))
    _ref = np.float64(np.copy(ref))
    
    mse = np.mean((_img - _ref) ** 2)
   
    if mse == 0:
        return 100
    PIXEL_MAX = 255.0
    return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))

کد کامل برنامه:

#!/usr/bin/env python3
# coding: utf-8

import cv2
import numpy as np
import math

def show_image_and_wait(title, image, resize_scale=None):
    if resize_scale:
        disp_img = cv2.resize(image, (0, 0), None, resize_scale, resize_scale)
    else:
        disp_img = image
    key = 0
    while True:
        cv2.imshow(title, disp_img)
        
        key = cv2.waitKey(1) & 0xFF
        if key == 27:
            print('<ESC> pressed')
            break
        elif key == ord('q'):
            print('<q> pressed')
            break
            
        if cv2.getWindowProperty(title ,cv2.WND_PROP_VISIBLE) < 1:    
            print('Close (X) button clicked')            
            break  
    cv2.destroyWindow(title)


def concat_images(img1, img2, direction='H'):
    assert img1.shape == img2.shape , "Both images MUST be in same shape!"
    if direction == 'H':
        new_img = cv2.hconcat([img1, img2])
    elif direction == 'V':
        new_img = cv2.vconcat([img1, img2])
    else:
        print('Plese use [V] or [H] for direction')
    return new_img


def snr(img, ref):
    #convert imput images to float64
    _img = np.float64(np.copy(img))
    _ref = np.float64(np.copy(ref))
    
    sum_img = np.sum((_img)**2)
    sum_noise = np.sum((_img - _ref)**2)
    if sum_noise == 0:
        return 100
    return 10 * math.log10(sum_img / sum_noise)


def psnr(img, ref):
    #convert imput images to float64
    _img = np.float64(np.copy(img))
    _ref = np.float64(np.copy(ref))
    
    mse = np.mean((_img - _ref) ** 2)
   
    if mse == 0:
        return 100
    PIXEL_MAX = 255.0
    return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))


img_path = 'oggy.jpeg'
img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
if img is None:
        raise ValueError('Coud not open or find the image')
        
noisy_img_path = "oggy_noise_10.jpg"
noisy_img = cv2.imread(noisy_img_path, cv2.IMREAD_UNCHANGED)
if noisy_img is None:
        raise ValueError('Coud not open or find the image')


psnr_val = psnr(noisy_img, img)
snr_val = snr(noisy_img, img)
print('PSNR: {0}'.format(psnr_val))
print('SNR: {0}'.format(snr_val))


disp_img = noisy_img.copy()
cv2.putText(disp_img,'SNR:  {0: 4.5f}'.format(snr_val), (60,40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(disp_img,'PSNR: {0: 4.5f}'.format(psnr_val), (60,80), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

show_image_and_wait('www.mh-salari.me/snr-and-psnr-in-python', disp_img, 0.5)

از نظر من نیازی به توضیح بیشتر نیست، اگر جایی از کد رو متوجه نشدید کامنت بذارید.

و در نهایت صحت سنجی خروجی توابع ما در کنار دستور psnr نرم‌افزار octave:

پ.ن:

در نوشته‌ی قبلی [+اینجا] یاد گرفتیم که چطور نویز گاوسی رو به تصویرمون اضافه کنیم، برای ایجاد این تصاویر نویزی از همون برنامه‌ی قبلی استفاده کردم.

+اینجا در مورد تابعی که برای کنار هم قرار دادن تصاویر استفاده کردم توضیح دادم.

منابع:

http://bigwww.epfl.ch/sage/soft/snr/

https://octave.sourceforge.io/image/function/psnr.html

https://dsp.stackexchange.com/questions/11326/difference-between-snr-and-psnr


نویسنده: محمد حسین سالاری

موارد مشابه

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *