Parcourir la source

Extracts and display user clicks script developed

Jérôme BUISINE il y a 4 ans
Parent
commit
d5e84a64cc

+ 1 - 0
.gitignore

@@ -108,4 +108,5 @@ venv.bak/
 expe_data
 dataset
 extracted_data
+images
 .vscode

+ 2 - 0
custom_config.py

@@ -8,6 +8,8 @@ data_expe_folder                = 'data_expe'
 data_augmented_filename         = 'augmented_dataset.csv'
 
 extracted_data_folder           = 'extracted_data'
+all_subjects_data_folder        = 'all_subjects'
+media_data_folder               = 'images'
 
 # variables
 image_scene_size                = (800, 800)

+ 0 - 0
data_processing/generate_datasert_by_zones.py


+ 1 - 1
display/display_scenes_info.py

@@ -35,11 +35,11 @@ def main():
 
     data = scenes_data[p_scene]
     
+    # set title and zone axis
     plt.title(p_scene, 'with data :', p_data)
 
     for x_i, x in enumerate(cfg.zone_coodinates):
         plt.plot([x_i * 200, x_i * 200], [0, 800], color='red')
-
     
     for y_i, y in enumerate(cfg.zone_coodinates):
         plt.plot([0, 800], [y_i * 200, y_i * 200], color='red')

+ 175 - 0
extracts/extract_expe_info_subject_scenes.py

@@ -0,0 +1,175 @@
+# main imports
+import sys, os, argparse
+import math
+import numpy as np
+import pickle
+import time
+
+# processing imports
+from PIL import Image
+
+import matplotlib.pyplot as plt
+import scipy.stats as stats
+
+# modules imports
+sys.path.insert(0, '') # trick to enable import of main folder module
+
+import custom_config as cfg
+import utils as utils_functions
+
+# variables
+data_expe_folder          = cfg.data_expe_folder
+position_file_pattern     = cfg.position_file_pattern
+click_line_pattern        = cfg.click_line_pattern
+
+min_x                     = cfg.min_x_coordinate
+min_y                     = cfg.min_y_coordinate
+image_scene_size          = cfg.image_scene_size
+scene_width, scene_height = image_scene_size
+
+
+def main():
+
+    parser = argparse.ArgumentParser(description="Compute expe data into output file")
+
+    parser.add_argument('--n', type=int, help="`n` first clicks", required=True)
+    parser.add_argument('--folder', type=str, help="output folder expected", required=True)
+    parser.add_argument('--reverse', type=int, help="reverse or not y axis clicks", default=False)
+
+    args = parser.parse_args()
+
+    p_n        = args.n
+    p_folder   = args.folder
+    p_reverse  = bool(args.reverse)
+
+    # list all folders
+    subjects = os.listdir(data_expe_folder)
+
+    print('Number of subjects', len(subjects))
+
+    output_folder_path = os.path.join(cfg.media_data_folder, p_folder)
+
+    if not os.path.exists(output_folder_path):
+        os.makedirs(output_folder_path)
+
+    # keep scene_data in memory
+    scenes_data = {}   
+
+    for scene in cfg.scenes_names:
+        
+        scenes_data[scene] = {}
+        # construct for each scene
+        scenes_data[scene]['x'] = []
+        scenes_data[scene]['y'] = []   
+
+        
+    for index, subject in enumerate(subjects):
+        
+        subject_folder = os.path.join(data_expe_folder, subject)
+        data_files = os.listdir(subject_folder)
+
+        pos_file = [f for f in data_files if position_file_pattern in f][0]
+        
+        pos_filepath = os.path.join(subject_folder, pos_file)
+
+        previous_path_scene = ""
+        path_scene          = ""
+        new_scene           = True
+        number_of_scenes    = 0
+        counter             = 0
+        scene_name          = ""
+
+        print('Extract images clicks of subject', subject)
+
+        # open pos file and extract click information 
+        with open(pos_filepath, 'r') as f:
+            
+            x_points = []
+            y_points = []
+
+            for line in f.readlines():
+                
+                if click_line_pattern in line and scene_name in cfg.scenes_names:
+                    
+                    x, y = utils_functions.extract_click_coordinate(line)
+
+                    p_x = x - min_x
+                    p_y = y - min_y
+
+                    # only accept valid coordinates
+                    if utils_functions.check_coordinates(p_x, p_y):
+                        
+                        if p_reverse:
+                            # add reversed points here
+                            p_y = scene_height - p_y
+
+                        if counter < p_n:
+
+                            # add points here
+                            x_points.append(p_x)
+                            y_points.append(p_y)
+
+                            counter += 1
+                
+                elif click_line_pattern not in line:
+                    path_scene = line
+
+                if previous_path_scene != path_scene:
+
+                    previous_path_scene = path_scene
+                    new_scene = True
+                    scene_name = path_scene.split('/')[4]
+
+                    if scene_name in cfg.scenes_names:
+                        number_of_scenes += 1
+
+                        if previous_path_scene != "":
+
+                            subject_path = os.path.join(output_folder_path, subject)
+                            
+                            if not os.path.exists(subject_path):
+                                os.makedirs(subject_path)     
+
+                            output_image_name = subject + '_' + scene_name + '_' + str(p_n) + '.png'
+                            img_path = os.path.join(subject_path, output_image_name)
+
+                            title = subject + ' - ' + scene_name + ' (' + str(p_n) + ' clicks)'
+
+                            # save image plot
+                            utils_functions.save_img_plot(scene_name, x_points, y_points, title, img_path)
+
+                            # save scene data
+                            scenes_data[scene_name]['x'] = scenes_data[scene_name]['x'] + x_points
+                            scenes_data[scene_name]['y'] = scenes_data[scene_name]['y'] + y_points
+
+                else:
+                    new_scene = False
+
+                if new_scene:
+                    counter = 0
+
+    all_path_folder = os.path.join(output_folder_path, cfg.all_subjects_data_folder)
+
+    print('Merge images clicks of subjects into', all_path_folder)
+    if not os.path.exists(all_path_folder):
+        os.makedirs(all_path_folder)
+
+    for k, v in scenes_data.items():
+
+        current_x_points = v['x']
+        current_y_points = v['y']
+
+        title = k + ' scene with all subjects (with ' + str(p_n) + ' clicks per subject)'
+
+        img_filename = cfg.all_subjects_data_folder + '_' + k + '_' + str(p_n) + '.png'
+        img_path = os.path.join(all_path_folder, img_filename)
+
+        # save figure `all` subjects `p_n` clicks
+        utils_functions.save_img_plot(k, current_x_points, current_y_points, title, img_path)
+
+
+    print('Images are saved into', output_folder_path)
+    
+
+if __name__== "__main__":
+    main()

+ 205 - 0
extracts/extract_expe_info_subject_zones.py

@@ -0,0 +1,205 @@
+# main imports
+import sys, os, argparse
+import math
+import numpy as np
+import pickle
+
+# processing imports
+from PIL import Image
+
+import matplotlib.pyplot as plt
+import scipy.stats as stats
+
+# modules imports
+sys.path.insert(0, '') # trick to enable import of main folder module
+
+import custom_config as cfg
+import utils as utils_functions
+
+# variables
+data_expe_folder          = cfg.data_expe_folder
+position_file_pattern     = cfg.position_file_pattern
+click_line_pattern        = cfg.click_line_pattern
+
+min_x                     = cfg.min_x_coordinate
+min_y                     = cfg.min_y_coordinate
+image_scene_size          = cfg.image_scene_size
+scene_width, scene_height = image_scene_size
+
+
+def main():
+
+    parser = argparse.ArgumentParser(description="Compute expe data into output file")
+
+    parser.add_argument('--n', type=int, help="`n` first clicks", required=True)
+    parser.add_argument('--folder', type=str, help="output folder expected", required=True)
+    parser.add_argument('--reverse', type=int, help="reverse or not y axis clicks", default=False)
+
+    args = parser.parse_args()
+
+    p_n        = args.n
+    p_folder   = args.folder
+    p_reverse  = bool(args.reverse)
+
+    print(p_reverse)
+
+    # list all folders
+    subjects = os.listdir(data_expe_folder)
+
+    print('Number of subjects', len(subjects))
+
+    output_folder_path = os.path.join(cfg.media_data_folder, p_folder)
+
+    if not os.path.exists(output_folder_path):
+        os.makedirs(output_folder_path)
+
+    # keep scene_data in memory
+    scenes_data = {}   
+
+    for scene in cfg.scenes_names:
+        
+        zones_list = {}
+
+        for zone_index in cfg.zones_indices:
+
+            zones_list[zone_index] = {}
+            # construct for each scene
+            zones_list[zone_index]['x'] = []
+            zones_list[zone_index]['y'] = []
+
+        scenes_data[scene] = zones_list
+
+    for _, subject in enumerate(subjects):
+        
+        subject_folder = os.path.join(data_expe_folder, subject)
+        data_files = os.listdir(subject_folder)
+
+        pos_file = [f for f in data_files if position_file_pattern in f][0]
+        
+        pos_filepath = os.path.join(subject_folder, pos_file)
+
+        previous_path_scene = ""
+        path_scene          = ""
+        new_scene           = True
+        number_of_scenes    = 0
+        scene_name          = ""
+
+        print('Extract images clicks of subject', subject)
+
+        # open pos file and extract click information 
+        with open(pos_filepath, 'r') as f:
+
+            # for each subject check `p_n` on each zone
+            zones_filled = {}
+            zones_clicks_of_subject = {}
+
+            for zone_index in cfg.zones_indices:
+                zones_filled[zone_index] = 0
+
+                zones_clicks_of_subject[zone_index] = {}
+                zones_clicks_of_subject[zone_index]['x'] = []
+                zones_clicks_of_subject[zone_index]['y'] = []
+
+            for line in f.readlines():
+                
+                if click_line_pattern in line and scene_name in cfg.scenes_names:
+                    
+                    x, y = utils_functions.extract_click_coordinate(line)
+
+                    p_x = x - min_x
+                    p_y = y - min_y
+
+                    # only accept valid coordinates (need to substract `x_min` and `y_min` before check)
+                    if utils_functions.check_coordinates(p_x, p_y):
+                        
+                        if p_reverse:
+                            # add reversed points here
+                            p_y = scene_height - p_y
+
+                        # get zone indice
+                        zone_index = utils_functions.get_zone_index(p_x, p_y)
+
+                        # check number of points saved for this specific zone
+                        # add only if wished
+                        if zones_filled[zone_index] < p_n:
+
+                            zones_clicks_of_subject[zone_index]['x'].append(p_x)
+                            zones_clicks_of_subject[zone_index]['y'].append(p_y)
+                            zones_filled[zone_index] += 1
+                
+                elif click_line_pattern not in line:
+                    path_scene = line
+
+                if previous_path_scene != path_scene:
+
+                    previous_path_scene = path_scene
+                    new_scene = True
+                    scene_name = path_scene.split('/')[4]
+
+                    if scene_name in cfg.scenes_names:
+                        number_of_scenes += 1
+
+                        if previous_path_scene != "":
+
+                            subject_path = os.path.join(output_folder_path, subject)
+                            
+                            if not os.path.exists(subject_path):
+                                os.makedirs(subject_path)     
+
+                            output_image_name = subject + '_' + scene_name + '_' + str(p_n) + '.png'
+                            img_path = os.path.join(subject_path, output_image_name)
+
+                            title = subject + ' - ' + scene_name + ' (' + str(p_n) + ' clicks)'
+
+                            # save image plot
+                            x_points = zones_clicks_of_subject[zone_index]['x']
+                            y_points = zones_clicks_of_subject[zone_index]['y']
+
+                            utils_functions.save_img_plot(scene_name, x_points, y_points, title, img_path)
+
+                            # save scene data
+                            for i in cfg.zones_indices:
+                                scenes_data[scene_name][i]['x'] = scenes_data[scene_name][i]['x'] + x_points
+                                scenes_data[scene_name][i]['y'] = scenes_data[scene_name][i]['y'] + y_points
+
+                        # reinit zones list
+                        for zone_index in cfg.zones_indices:
+                            zones_filled[zone_index] = 0
+
+                            zones_clicks_of_subject[zone_index] = {}
+                            zones_clicks_of_subject[zone_index]['x'] = []
+                            zones_clicks_of_subject[zone_index]['y'] = []
+
+                else:
+                    new_scene = False
+
+    all_path_folder = os.path.join(output_folder_path, cfg.all_subjects_data_folder)
+
+    if not os.path.exists(all_path_folder):
+        os.makedirs(all_path_folder)
+
+
+    print('Merge images clicks of subjects into', all_path_folder)
+    for k, v in scenes_data.items():
+
+        current_x_points = [] 
+        current_y_points = []
+
+        for i in cfg.zones_indices:
+            current_x_points = current_x_points + v[i]['x']
+            current_y_points = current_y_points + v[i]['y']
+
+        title = k + ' scene with all subjects (with ' + str(p_n) + ' clicks per subject)'
+
+        img_filename = cfg.all_subjects_data_folder + '_' + k + '_' + str(p_n) + '.png'
+        img_path = os.path.join(all_path_folder, img_filename)
+
+        # save figure `all` subjects `p_n` clicks
+        utils_functions.save_img_plot(k, current_x_points, current_y_points, title, img_path)
+
+
+    print('Images are saved into', output_folder_path)
+    
+
+if __name__== "__main__":
+    main()

+ 6 - 3
generate/extract_expe_info_scenes.py

@@ -78,12 +78,15 @@ def main():
                     
                     x, y = utils_functions.extract_click_coordinate(line)
 
+                    p_x = x - min_x
+                    p_y = y - min_y
+                    
                     # only accept valid coordinates
-                    if utils_functions.check_coordinates(x, y):
+                    if utils_functions.check_coordinates(p_x, p_y):
                         
                         if counter < p_n:
-                            scenes[scene_name]['x'].append(x - min_x)
-                            scenes[scene_name]['y'].append(y - min_y)
+                            scenes[scene_name]['x'].append(p_x)
+                            scenes[scene_name]['y'].append(p_y)
                             counter += 1
                 
                 elif click_line_pattern not in line:

+ 11 - 4
generate/extract_expe_info_zones_scenes.py

@@ -3,6 +3,7 @@ import sys, os, argparse
 import math
 import numpy as np
 import pickle
+import time
 
 # processing imports
 import matplotlib.pyplot as plt
@@ -87,15 +88,21 @@ def main():
                 if click_line_pattern in line and scene_name in cfg.scenes_names:
                     
                     x, y = utils_functions.extract_click_coordinate(line)
+         
+                    p_x = x - min_x
+                    p_y = y - min_y
 
                     # only accept valid coordinates
-                    if utils_functions.check_coordinates(x, y):
-                        
-                        p_x = x - min_x
-                        p_y = y - min_y
+                    if utils_functions.check_coordinates(p_x, p_y):
 
+                        # TODO : need to reverse `y` axis for correct zone index (zone 0 on from top left)
                         zone_index = utils_functions.get_zone_index(p_x, p_y)
 
+                        plt.scatter([p_x], [p_y])
+                        plt.show()
+
+                        time.sleep(2)
+
                         # check number of points saved for this specific zone
                         # add only if wished
                         if zones_filled[zone_index] < p_n:

+ 0 - 87
generate/generate_data_augmentation_zone.py

@@ -1,87 +0,0 @@
-# main imports
-import os, sys
-import argparse
-import pickle
-
-# image processing imports
-from PIL import Image
-
-from ipfml.processing import transform, segmentation
-from ipfml import utils
-
-# modules imports
-sys.path.insert(0, '') # trick to enable import of main folder module
-
-import custom_config as cfg
-from modules.utils import data as dt
-
-# getting configuration information
-zone_folder             = cfg.zone_folder
-min_max_filename        = cfg.min_max_filename_extension
-
-# define all scenes values
-scenes_list             = cfg.scenes_names
-scenes_indexes          = cfg.scenes_indices
-path                    = cfg.dataset_path
-zones                   = cfg.zones_indices
-seuil_expe_filename     = cfg.seuil_expe_filename
-
-output_data_folder      = cfg.output_data_folder
-
-
-image_scene_size        = cfg.image_scene_size
-image_zone_size         = cfg.image_zone_size
-possible_point_zone     = cfg.possible_point_zone
-
-
-def main():
-
-    parser = argparse.ArgumentParser(description="Compute and prepare data augmentation of scenes")
-
-    parser.add_argument('--data', type=str, help="object filename saved using pickle", required=True)
-    parser.add_argument('--scene', type=str, help="scene name to display click information", required=True, choices=cfg.scenes_names)
-    parser.add_argument('--n', type=int, help="number of clics per zone wished")
-
-    args = parser.parse_args()
-    
-    p_data   = args.data
-    p_scene  = args.scene
-    p_n      = args.n
-
-    # load data extracted by zones
-    fileObject = open(p_data, 'rb')  
-    scenes_data = pickle.load(fileObject) 
-
-    scene_data = scenes_data[p_scene]
-    # get scenes list
-    scenes = os.listdir(path)
-
-    # remove min max file from scenes folder
-    scenes = [s for s in scenes if min_max_filename not in s]
-
-        # go ahead each scenes
-    for folder_scene in scenes:
-
-        scene_path = os.path.join(path, folder_scene)
-
-        # construct each zones folder name
-        zones_folder = []
-        zones_threshold = []
-
-        # get zones list info
-        for index in zones:
-            index_str = str(index)
-            if len(index_str) < 2:
-                index_str = "0" + index_str
-
-            current_zone = "zone"+index_str
-            zones_folder.append(current_zone)
-
-            zone_path = os.path.join(scene_path, current_zone)
-
-            with open(os.path.join(zone_path, seuil_expe_filename)) as f:
-                zones_threshold.append(int(f.readline()))
-
-
-if __name__== "__main__":
-    main()

+ 179 - 0
generate/generate_data_augmentation_zones.py

@@ -0,0 +1,179 @@
+# main imports
+import os, sys
+import argparse
+import pickle
+import random
+import numpy as np
+import math
+
+# image processing imports
+from PIL import Image
+
+from ipfml.processing import transform, segmentation
+from ipfml import utils
+
+# modules imports
+sys.path.insert(0, '') # trick to enable import of main folder module
+
+import custom_config as cfg
+from modules.utils import data as dt
+
+import utils as utils_functions
+
+# getting configuration information
+zone_folder             = cfg.zone_folder
+min_max_filename        = cfg.min_max_filename_extension
+
+# define all scenes values
+scenes_list             = cfg.scenes_names
+scenes_indexes          = cfg.scenes_indices
+path                    = cfg.dataset_path
+zones                   = cfg.zones_indices
+seuil_expe_filename     = cfg.seuil_expe_filename
+
+output_data_folder      = cfg.output_data_folder
+
+image_scene_size        = cfg.image_scene_size
+image_zone_size         = cfg.image_zone_size
+possible_point_zone     = cfg.possible_point_zone
+
+
+def main():
+
+    parser = argparse.ArgumentParser(description="Compute and prepare data augmentation of scenes")
+
+    parser.add_argument('--data', type=str, help="object filename saved using pickle", required=True)
+    parser.add_argument('--scene', type=str, help="scene name to display click information", required=True, choices=cfg.scenes_names)
+    parser.add_argument('--n', type=int, help="number of clics per zone wished")
+    parser.add_argument('--images', type=int, help="number of images (with estimated thresholds) wished by scene")
+    parser.add_argument('--output', type=str, help="output file with new thresholds data")
+
+    args = parser.parse_args()
+    
+    p_data   = args.data
+    p_scene  = args.scene
+    p_n      = args.n
+    p_images = args.images
+    p_output = args.output
+
+    # load data extracted by zones
+    fileObject = open(p_data, 'rb')  
+    scenes_data = pickle.load(fileObject) 
+
+    # get clicks data of specific scene
+    scene_data = scenes_data[p_scene]
+
+    # getting image zone size and usefull information
+    zone_width, zone_height = image_zone_size
+    scene_width, scene_height = image_scene_size
+    nb_x_parts = math.floor(scene_width / zone_width)
+
+    # get scenes list
+    scenes = os.listdir(path)
+
+    # remove min max file from scenes folder
+    scenes = [s for s in scenes if min_max_filename not in s]
+
+    # go ahead each scenes in order to get threshold
+    for folder_scene in scenes:
+
+        scene_path = os.path.join(path, folder_scene)
+
+        # construct each zones folder name
+        zones_folder = []
+        zones_threshold = []
+
+        # get zones list info
+        for index in zones:
+            index_str = str(index)
+            if len(index_str) < 2:
+                index_str = "0" + index_str
+
+            current_zone = "zone"+index_str
+            zones_folder.append(current_zone)
+
+            zone_path = os.path.join(scene_path, current_zone)
+
+            with open(os.path.join(zone_path, seuil_expe_filename)) as f:
+                zones_threshold.append(int(f.readline()))
+
+        # generate a certain number of images
+        for i in range(p_images):
+            
+            ###########################################
+            # Compute weighted threshold if necessary #
+            ###########################################           
+
+            ##############################
+            # 1. Get random point from possible position
+            ##############################
+            possible_x, possible_y = possible_point_zone
+
+            p_x, p_y = (random.randrange(possible_x), random.randrange(possible_y))
+
+            ##############################
+            # 2. Get zone indices of this point (or only one zone if `%` 200)
+            ##############################
+
+            # coordinate of specific zone, hence use threshold of zone
+            if p_x % zone_width == 0 and p_y % zone_height == 0:
+                    
+                zone_index = utils_functions.get_zone_index(p_x, p_y)
+
+                final_threshold = int(zones_threshold[zone_index])
+            else:
+                # get zone identifiers of this new zones (from endpoints)
+                p_top_left = (p_x, p_y)
+                p_top_right = (p_x + zone_width, p_y)
+                p_bottom_right = (p_x + zone_width, p_y + zone_height)
+                p_bottom_left = (p_x, p_y + zone_height)
+
+                points = [p_top_left, p_top_right, p_bottom_right, p_bottom_left]
+
+                p_zones_indices = []
+                
+                # for each points get threshold information
+                for p in points:
+                    x, y = p
+
+                    zone_index = utils_functions.get_zone_index(x, y)
+                    p_zones_indices.append(zone_index)
+
+                # 2.3. Compute area of intersected zones (and weights)
+                # get proportions of pixels of img into each zone
+                overlaps = []
+
+                p_x_max = p_x + zone_width
+                p_y_max = p_y + zone_height
+
+                for index, zone_index in enumerate(p_zones_indices):
+                    x_zone = (zone_index % nb_x_parts) * zone_width
+                    y_zone = (math.floor(zone_index / nb_x_parts)) * zone_height
+
+                    x_max_zone = x_zone + zone_width
+                    y_max_zone = y_zone + zone_height
+
+                    # computation of overlap
+                    # x_overlap = max(0, min(rect1.right, rect2.right) - max(rect1.left, rect2.left))
+                    # y_overlap = max(0, min(rect1.bottom, rect2.bottom) - max(rect1.top, rect2.top))
+                    x_overlap = max(0, min(x_max_zone, p_x_max) - max(x_zone, p_x))
+                    y_overlap = max(0, min(y_max_zone, p_y_max) - max(y_zone, p_y))
+
+                    overlapArea = x_overlap * y_overlap
+                    overlaps.append(overlapArea)
+
+                overlapSum = sum(overlaps)
+
+                # area weights are saved into proportions
+                proportions = [item / overlapSum for item in overlaps]
+
+                # 2.4. Count number of clicks present into each zones intersected (and weights)
+                
+
+                # 2.5. Compute final threshold of `x` and `y` using `3` and `4` steps
+                p_thresholds = np.array(zones_threshold)[p_zones_indices]
+
+            # 3. Save this new entry into .csv file (scene_name; x; y; threshold)
+
+if __name__== "__main__":
+    main()

+ 34 - 3
utils.py

@@ -1,5 +1,9 @@
 # main imports
-import sys, math
+import sys, math, os
+
+# processing imports
+import matplotlib.pyplot as plt
+from PIL import Image
 
 # modules imports
 sys.path.insert(0, '') # trick to enable import of main folder module
@@ -23,10 +27,10 @@ def get_zone_index(p_x, p_y):
 
 def check_coordinates(p_x, p_y):
 
-    if p_x < min_x or p_y < min_y:
+    if p_x < 0 or p_y < 0:
         return False
         
-    if p_x >= min_x + scene_width or p_y >= min_y + scene_height:
+    if p_x >= scene_width or p_y >= scene_height:
         return False
     
     return True
@@ -39,3 +43,30 @@ def extract_click_coordinate(line):
     p_x, p_y = (int(data[0]), int(data[1]))
 
     return (p_x, p_y)
+
+
+def save_img_plot(scene_name, x_points, y_points, title, img_path):
+
+    folder_scene = os.path.join(cfg.dataset_path, scene_name)
+
+    images = [img for img in os.listdir(folder_scene) if '.png' in img]
+    images = sorted(images)
+
+    first_image_path = os.path.join(folder_scene, images[0])
+    img = Image.open(first_image_path)
+
+    plt.rcParams["figure.figsize"] = (20, 20)
+
+    # Save here data information about subject
+    plt.title(title, fontsize=30)
+    plt.imshow(img)
+    plt.scatter(x_points, y_points, color='red')
+
+    for x_i, x in enumerate(cfg.zone_coodinates):
+        plt.plot([x_i * 200, x_i * 200], [0, 800], color='blue')
+
+    for y_i, y in enumerate(cfg.zone_coodinates):
+        plt.plot([0, 800], [y_i * 200, y_i * 200], color='blue')
+
+    plt.axis('off')
+    plt.savefig(img_path, dpi=100)