Search code examples
pythonfunctionscopenestedpython-nonlocal

How to call a parent variable in a nested function


I've written a denoising function with cv2 and concurrent.futures, to be applied on both my training and test image data.

The functions (at current) are as follows:

def denoise_single_image(img_path):
    nonlocal data
    img = cv2.imread(f'../data/jpeg/{data}/{img_path}')
    dst = cv2.fastNlMeansDenoising(img, 10,10,7,21)
    cv2.imwrite(f'../processed_data/jpeg/{data}/{img_path}', dst)
    print(f'{img_path} denoised.')
 
def denoise(data):
    img_list = os.listdir(f'../data/jpeg/{data}')
    with concurrent.futures.ProcessPoolExecutor() as executor:
        tqdm.tqdm(executor.map(denoise_single_image, img_list))

data is to be either train or test, as necessary; img_list is a list of all image names in that directory.

I need to create the denoise_single_image() function before denoise(), otherwise denoise() won't recognize it; but I need to define data before I create denoise_single_image(). This seems to be a catch-22, unless I can figure out how to tell denoise_single_image() to refer to a variable that exists one level up.

nonlocal doesn't work because it assumes that data has already been defined in this environment. Is there any way to get this to work?


Solution

  • You can change the iterable in executor.map to be a tuple of arguments, which can then split in your other function.

    executor.map(denoise_single_image, ((img_path, data) for img_path in img_list))
    
    def denoise_single_image(inputs):
        img_path, data = inputs
        # etc
    

    But in your case I would just modify the individual image paths like so

    executor.map(denoise_single_image, (f'jpeg/{data}/{img_path}' for img_path in img_list))
    
    def denoise_single_image(img_path):
        img = cv2.imread(f'../data/{img_path}')
        dst = cv2.fastNlMeansDenoising(img, 10,10,7,21)
        cv2.imwrite(f'../processed_data/{img_path}', dst)
        print(f'{img_path.split('/')[-1]} denoised.')