Итерация по двум изображениям попиксельно в Numpy (со случайным условием) - PullRequest
1 голос
/ 04 февраля 2020
import random
def sp_noise(image,prob):
    '''
    Add salt and pepper noise to image
    prob: Probability of the noise
    '''
    output = np.zeros(image.shape,np.uint8)
    thres = 1 - prob 
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            rdn = random.random()
            if rdn < prob:
                output[i][j] = 0
            elif rdn > thres:
                output[i][j] = 255
            else:
                output[i][j] = image[i][j]
    return output

Вот чего я хочу достичь. Я понимаю, что эта функция не использует векторизацию, но я не понимаю, как я мог бы избавиться от циклов в этом случае. Если бы существовало условие для значений пикселей, это было бы тривиально. Но в этом случае нет никаких условий для индексов или значений пикселей, мне просто нужно либо сохранить значение пикселя, либо, в зависимости от значения случайной величины, установить его в 0 или 1.

Как мне векторизовать это?

Ответы [ 2 ]

5 голосов
/ 04 февраля 2020

Вы можете вызвать np.random.random с дополнительным параметром size, чтобы получить целый массив случайных чисел с плавающей точкой. Затем используйте np.where и индексирование логического массива для доступа ко всем пикселям, соответствующим одному из условий.

Это будет мое решение, включая OpenCV для загрузки и отображения изображений и простого анализа производительности:

import cv2
import numpy as np
import time

def sp_noise(image, prob):
    output = np.zeros(image.shape, np.uint8)
    thres = 1 - prob
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            rdn = np.random.random()
            if rdn < prob:
                output[i][j] = 0
            elif rdn > thres:
                output[i][j] = 255
            else:
                output[i][j] = image[i][j]
    return output

def sp_noise_vec(image, prob):
    output = image.copy()
    thres = 1 - prob
    rdn = np.random.random(image.shape[:2])
    output[np.where(rdn < prob)] = 0
    output[np.where(rdn > thres)] = 255
    return output

img = cv2.imread('path/to/your/image.png')

tic = time.perf_counter()
out = sp_noise(img, 0.1)
toc = time.perf_counter()
print('Duration loop: ', toc - tic)

tic = time.perf_counter()
out_vec = sp_noise_vec(img, 0.1)
toc = time.perf_counter()
print('Duration vectorized: ', toc - tic)

cv2.imshow('img', img)
cv2.imshow('out', out)
cv2.imshow('out_vec', out_vec)
cv2.waitKey(0)
cv2.destroyAllWindows()

Вывод изображений сопоставим. Для некоторых 400 x 400 RGB-изображений я получаю следующее время:

Duration loop:        0.21099094100000004
Duration vectorized:  0.004011090000000106

Надеюсь, это поможет!

----------------------------------------
System information
----------------------------------------
Platform:    Windows-10-10.0.16299-SP0
Python:      3.8.1
NumPy:       1.18.1
OpenCV:      4.1.2
----------------------------------------
1 голос
/ 04 февраля 2020

Не уверен, что это даст точно такой же результат - поскольку есть вероятность (очень мала), что вы можете ударить пиксель солью, а затем снова тот же пиксель снова перцем - но я попробовал другой подход. Он также генерирует ровно половину соли и половину перца, что обычно делают другие растворы только при усреднении по большому количеству образцов. Возможно, скорость и экономия памяти стоят неточностей - YMMV: -)

Сначала определите, сколько пикселей будет подвержено шуму, назовите его N. Затем сгенерируйте N / 2 пары координат [x, y] и установите соответствующий пиксель в выходном изображении черным, затем сгенерируйте еще N / 2 пары координат [x, y] и установите их на белый.

import numpy as np

def me(image,prob): 
    h, w = image.shape[:2]
    # Number of noise affected pixels
    N = int(w * h * prob)
    # Half salt
    image[np.random.randint(0,h,int(N/2)), np.random.randint(0,w,int(N/2))] = 255
    # Half pepper
    image[np.random.randint(0,h,int(N/2)), np.random.randint(0,w,int(N/2))] = 0 
    return image 

# Make solid grey start image
image = np.full((400,400), 128, dtype=np.uint8)

С p=0.1 и p=0.01 я получаю:

%timeit me(image,0.1)                                                                               
296 µs ± 2.48 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit me(image,0.01)                                                                              
42.2 µs ± 933 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

enter image description here

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...