Source code for ImageProcessing

######################################################################
#          O  o   o o-o   o--o   o-o  o   o o--o o-o   o--o          #
#         / \ |\  | |  \  |   | o   o |\ /| |    |  \  |             #
#        o---o| \ | |   O O-Oo  |   | | O | O-o  |   O O-o           #
#        |   ||  \| |  /  |  \  o   o |   | |    |  /  |             #
#        o   oo   o o-o   o   o  o-o  o   o o--o o-o   o--o          #
######################################################################
#
# ANDROMEDE
# Copyright (C) 2023 Toulouse INP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details :
# <http://www.gnu.org/licenses/>.
#
######################################################################

import cv2
import time
import numpy as np
from PySide6.QtWidgets import QProgressDialog
from PySide6.QtGui import Qt
from Core import PreProcessing


[docs] def im_proc(im_in, params, mask_in, projective_matrix, size_processed, Ht): """Process one image, change color, transform as a function of homography. :param im_in: image to process :param color_choice: type of image color (RGB,HSV,gray) :param transformation: type of homography :param before: detection on transformed image (True/False) :param active_mask: use of mask (True/False) :param noise_filtering: filter image with a gaussian convolution (True/False) :param noise_size: size of the filter in pixel :param mask_in: mask defined by user :param projective_matrix: matrix defining the homography :param size_processed: size of the image after processing :param Ht: array with the shift to transformed coordinate to positive ones. :return: image processed :rtype: array """ if (params.dict['PP']['transformation'][0:10] == 'Homography' or params.dict['PP']['transformation'] == 'Camera position')\ and params.dict['PP']['before']: im_out = cv2.warpPerspective(im_in, Ht.dot(projective_matrix), (size_processed[1], size_processed[0])) else: im_out = crop(im_in, params) if params.dict['IP']['active_mask']: im_out = cv2.bitwise_and(im_out, im_out, mask=mask_in) if params.dict['IP']['color_choice'] == 1: im_out = cv2.cvtColor(im_out, cv2.COLOR_BGR2GRAY) elif params.dict['IP']['color_choice'] == 2: im_out = cv2.cvtColor(im_out, cv2.COLOR_BGR2HSV) else: im_out = im_out.copy(order='C') if params.dict['IP']['noise_filtering']: filtering_value = pow(int(params.dict['IP']['noise_size']), 2) im_out = cv2.medianBlur(im_out, filtering_value) return im_out
[docs] def background_computation(video, params): """Compute background image to eventually subtract :param video: analysed video object :param color_choice: type of image color (RGB,HSV,gray) :return: image of background :rtype: array """ params.outputConsole.appendPlainText('Compute background image') if params.dict['IP']['color_choice'] == 1: im_out = np.zeros(video.imgs[0].shape, dtype=float) n_frames = video.prop['window_frame'][1] - video.prop['window_frame'][0] for img in video.imgs: im_out += np.float32(img) im_out /= n_frames im_out = cv2.normalize(im_out, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) else: im_out = np.zeros((0, 0), dtype=float) params.outputConsole.appendPlainText('*** Error : Background image computation require Grey Images') return im_out
[docs] def background_computation_fullProcess(video, params): """Compute background image to eventually subtract, function used with a full process. :param video: analysed video object :param color_choice: type of image color (RGB,HSV,gray) :return: image of background :rtype: array """ params.outputConsole.appendPlainText('Compute background image') if params.dict['IP']['color_choice'] == 1: im_out = im_proc(video.cap.read()[1], params, video.masks['processed'], video.projective_matrix, video.process_dict['resolution'], video.process_dict['Ht_ROI']) n_frames = video.prop['window_frame'][1] - video.prop['window_frame'][0] im_out = np.float32(im_out) for id_frame in range(video.prop['window_frame'][0] + 1, video.prop['window_frame'][1], video.prop['frame_step']): image = im_proc(video.cap.read()[1], params, video.masks['processed'], video.projective_matrix, video.process_dict['resolution'], video.process_dict['Ht_ROI']) im_out += np.float32(image) im_out /= n_frames im_out = cv2.normalize(im_out, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) else: im_out = np.zeros((0, 0), dtype=float) params.outputConsole.appendPlainText('*** Error : Background image computation require Grey Images') return im_out
[docs] def crop(image_in, params): """Crop frame as a function of the defined ROI :param im_in: image to process :param ROI: pixel of the transformed ROI :return: cropped image :rtype: array """ return image_in[params.dict['IP']['ROIymin']:params.dict['IP']['ROIymax'] + 1, params.dict['IP']['ROIxmin']:params.dict['IP']['ROIxmax'] + 1]
[docs] def initialize_image_processing(video, param): """Initialize image processing. Provide the resolution of transformed image and transform the first image. :param video: analysed video object :param ROI: pixel of the transformed ROI :param window_frame: temporal windows of analysis (in frame number) :param frame_step: number of frame interval for the analysis :param transformation: type of homography :param before: detection on transformed image (True/False) :param original_type: type of image :return: number of image to analyse :rtype: int :return: first processed image :rtype: array """ video.process_dict['corner'] = (param.dict['IP']['ROIymin'], param.dict['IP']['ROIxmin']) video.process_dict['resolution'] = (param.dict['IP']['ROIymax'] - param.dict['IP']['ROIymin'], param.dict['IP']['ROIxmax'] - param.dict['IP']['ROIxmin']) if video.masks['original'].shape[0] > 0: video.masks['processed'] = crop(video.masks['original'], param) video.prop['window_frame'] = param.dict['IP']['window_frame'] diff_frame = int(video.prop['window_frame'][1] - video.prop['window_frame'][0]) video.prop['frame_step'] = param.dict['IP']['frame_step'] if diff_frame % video.prop['frame_step']: video.prop['window_frame'][1] -= diff_frame % video.prop['frame_step'] video.outputConsole.appendPlainText('Adjust Frame Max to : ' + str(video.prop['window_frame'][1])) num_images = int(video.prop['window_frame'][1] - video.prop['window_frame'][0] + 1) video.cap.set(cv2.CAP_PROP_POS_FRAMES, video.prop['window_frame'][0]) video.current_img = 0 PreProcessing.compute_projective_matrix(video, param) PreProcessing.shift_image(video, param) # modify video properties as a function of transformation if (param.dict['PP']['transformation'][0:10] == 'Homography' or param.dict['PP']['transformation'] == 'Camera position') \ and param.dict['PP']['before']: video.process_dict['resolution'] = video.process_dict['resolution_transform_ROI'] first_image = im_proc(video.cap.read()[1], param, video.masks['processed'], video.projective_matrix, video.process_dict['resolution'], video.process_dict['Ht_ROI']) param.dict['IP']['original_type'] = first_image.dtype return num_images, first_image
[docs] def process_image_processing_all(video, param): """Process all selected images. Available for step-by-step analysis. :param video: analysed video object :param background_image: use of background (True/False) :return: stack of processed images :rtype: list of array """ elapsed_time = time.time() video.outputConsole.appendPlainText('Preprocessing video...') progress = QProgressDialog('Starting video pre-processing', 'Stop', 0, 100) progress.setMinimumDuration(0) progress.setWindowModality(Qt.WindowModal) num_images, image = initialize_image_processing(video, param) video.imgs = [image] progress.setWindowTitle('Cropping/filtering') # Crop/Filtering images for i in range(video.prop['window_frame'][0], video.prop['window_frame'][1], video.prop['frame_step']): progress.setLabelText('Processing frame ' + str(i)) for skip in range(video.prop['frame_step'] - 1): video.cap.read()[1] image = im_proc(video.cap.read()[1], param, video.masks['processed'], video.projective_matrix, video.process_dict['resolution'], video.process_dict['Ht_ROI']) video.imgs.append(image) progress.setValue(int(100 * (i + 1 - video.prop['window_frame'][0]) / num_images)) if progress.wasCanceled(): video.prop['window_frame'][1] = i break # Background images if param.dict['IP']['background_image']: video.masks['background'] = background_computation(video, param) progress.setWindowTitle('Subtracting background') for i in range(num_images): progress.setLabelText('Processing frame ' + str(i)) progress.setValue(int(100 * (i + 1 + video.prop['window_frame'][0]) / num_images)) video.imgs[i] = cv2.absdiff(video.imgs[i], video.masks['background']) video.imgs[i] = cv2.normalize(video.imgs[i], None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) elapsed_time = time.time() - elapsed_time video.outputConsole.appendPlainText(str(len(video.imgs)) + ' images processed in ' + str(round(elapsed_time, 2)) + ' seconds\n') video.update_process_status(2)