######################################################################
# 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)