Skip to content

Commit a9dc238

Browse files
committed
Finished implementing intersection over union
1 parent 31578fb commit a9dc238

29 files changed

+337
-31
lines changed

App/Components/Optimalization/Optimaliation.h

+24-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#pragma once
12
/**
23
* This file is used for options optimalization
34
* For handlign the integration between the modules
@@ -17,20 +18,7 @@
1718
* Function crops the image and saves it to the temp folder in provided name
1819
* @param scale Parameter to define the scale of the saving
1920
*/
20-
void crop_save_image_sample(cv::Mat &out_img,std::vector<std::string> &images, std::string save_path,int scale=3){
21-
const std::string image_to_crop = images[rand() % images.size()];
22-
const int CROP_SIZE = 200;
23-
//Select random area on the image
24-
cv::Mat image = cv::imread(image_to_crop,cv::IMREAD_GRAYSCALE);
25-
int begin_x = rand() % (image.cols - CROP_SIZE);
26-
int begin_y = rand() % (image.rows - CROP_SIZE);
27-
//define region of interest
28-
cv::Rect roi(begin_x,begin_y,CROP_SIZE,CROP_SIZE);
29-
cv::Mat cropped = image(roi);
30-
Transformations::square_and_resize(cropped,CROP_SIZE*scale);
31-
cv::imwrite(save_path.c_str(),cropped);
32-
out_img = cropped;
33-
}
21+
void crop_save_image_sample(cv::Mat &out_img,std::vector<std::string> &images, std::string save_path,int scale=3);
3422

3523
/**
3624
* Function aims to sequentially change single option to find the best match for user image
@@ -40,7 +28,28 @@
4028
/**
4129
* Function reads the mask saved in image and saves it in variable
4230
*/
43-
void read_user_mask(){};
31+
void read_user_mask(std::string path_to_mask);
32+
33+
/**
34+
* Function compares user mask to the material mask specified
35+
*/
36+
double calculate_intersection_over_union(cv::Mat &image);
37+
38+
private:
39+
40+
//Function finds unique values in matrix and stores it in a vector
41+
void find_unique_values(cv::Mat &matrix, std::vector<int> &storage);
42+
43+
//Function adds one, or creates a new record with a certain object_id
44+
void sum_up_storage(std::map<int,int> &storage, int object_value);
45+
46+
/**
47+
* This function calculates maximum intersection over union on object (because many objects can overlap the user matrix)
48+
* returns pair, with first being max IoU, and second is the size of the object
49+
* @param object_id object id in USER MASK
50+
* @param matrix image/mask given to compare with USER MASK
51+
*/
52+
std::pair<double,int> calc_intersection_over_union_per_object(int object_id, cv::Mat &matrix);
4453

4554
cv::Mat user_mask_matrix; //OpenCV like connected components matrix for user mask
4655
AlgorithmOptions *options; //Algorithm options
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include "./Optimaliation.h"
2+
3+
4+
void Optimalization::crop_save_image_sample(cv::Mat &out_img,std::vector<std::string> &images, std::string save_path,int scale){
5+
const std::string image_to_crop = images[rand() % images.size()];
6+
const int CROP_SIZE = 200;
7+
//Select random area on the image
8+
cv::Mat image = cv::imread(image_to_crop,cv::IMREAD_GRAYSCALE);
9+
int begin_x = rand() % (image.cols - CROP_SIZE);
10+
int begin_y = rand() % (image.rows - CROP_SIZE);
11+
//define region of interest
12+
cv::Rect roi(begin_x,begin_y,CROP_SIZE,CROP_SIZE);
13+
cv::Mat cropped = image(roi);
14+
Transformations::square_and_resize(cropped,CROP_SIZE*scale);
15+
cv::imwrite(save_path.c_str(),cropped);
16+
out_img = cropped;
17+
}
18+
19+
void Optimalization::read_user_mask(std::string path_to_mask){
20+
cv::Mat mask = cv::imread(path_to_mask,cv::IMREAD_GRAYSCALE);
21+
mask.convertTo(this->user_mask_matrix, CV_32S);
22+
//I don't think I need to normalize it
23+
24+
}
25+
26+
double Optimalization::calculate_intersection_over_union(cv::Mat &image){
27+
//Check if user mask is specified
28+
if(this->user_mask_matrix.empty()){
29+
throw std::invalid_argument("Trying to calculate intersection without user mask specified");
30+
}
31+
//Check if image is specified
32+
if(image.empty()){
33+
throw std::invalid_argument("Argument image in intersection calculation is not specified");
34+
}
35+
//Detect how many unique objects are there in a user matrix
36+
std::vector<int> unique_in_user_matrix;
37+
this->find_unique_values(this->user_mask_matrix,unique_in_user_matrix);
38+
//Then, calculate the intersection per object, including it's size
39+
double val_sum = 0;
40+
int pixel_sum = 0;
41+
for(size_t x = 0; x < unique_in_user_matrix.size(); x++){
42+
std::pair<double,int> result = this->calc_intersection_over_union_per_object(unique_in_user_matrix[x],image);
43+
val_sum += result.first * result.second;
44+
pixel_sum += result.second;
45+
}
46+
return val_sum / pixel_sum;
47+
}
48+
49+
50+
//Private
51+
52+
53+
void Optimalization::find_unique_values(cv::Mat &matrix, std::vector<int> &storage){
54+
for (int x=0; x < matrix.rows; x++){
55+
for(int y = 0; y < matrix.cols; y++){
56+
int value = matrix.at<int>(x,y);
57+
if(value == -1 || value == 0)continue;
58+
if(std::find(storage.begin(), storage.end(), value) == storage.end() ){
59+
storage.push_back(value);
60+
}
61+
}
62+
}
63+
}
64+
65+
void Optimalization::sum_up_storage(std::map<int,int> &storage, int object_value){
66+
//Find if key already exists
67+
auto it = storage.find(object_value);
68+
if(it == storage.end()){
69+
storage.insert({object_value,1});
70+
return;
71+
}
72+
//If found
73+
it->second = it->second+1;
74+
}
75+
76+
//I KNOW THIS ALGORITHM CAN BE MORE OPTIMAL, BUT I DID IT THEY WAY I DID IT, SO SCREW IT
77+
//I mean, it could be reduced from in final version O(p(2n^2)), where p=nr of objects in user_mask, to just O(2n^2) ...
78+
std::pair<double,int> Optimalization::calc_intersection_over_union_per_object(int object_id, cv::Mat &matrix){
79+
//Working in O(2(n^2)) time, where n is width=height of matrix
80+
cv::Mat mask = this->user_mask_matrix == object_id;
81+
if(mask.size() != matrix.size()){
82+
throw std::invalid_argument("Error, trying to compare user mask, and argument mask, they are different in size");
83+
}
84+
//Creating a storage for calculating intersection over union
85+
std::map<int,int> intersection_storage;
86+
std::map<int,int> union_storage;
87+
int overall_matrix = 0;
88+
for(int x=0; x < mask.cols; x++){
89+
for(int y=0;y<mask.rows;y++){
90+
int val_mask = mask.at<int>(x,y);
91+
int val_matrix = matrix.at<int>(x,y);
92+
if(val_mask != 0){
93+
sum_up_storage(intersection_storage,val_matrix);
94+
overall_matrix += 1;
95+
continue;
96+
}
97+
sum_up_storage(union_storage,val_matrix);
98+
}
99+
}
100+
//Now, find the maximum value of intersection over union, here only the maximum value is in interest
101+
double max_val = 0;
102+
for(const auto &iter : intersection_storage){
103+
int key = iter.first;
104+
if(key == 1)continue; //Exclude background
105+
int to_add = -1;
106+
//Find the same key in union_storage
107+
auto union_val = union_storage.find(key);
108+
if(union_val == union_storage.end()){
109+
to_add = 0;
110+
}
111+
if(to_add == -1){
112+
to_add = union_val->second;
113+
}
114+
int intersec_value = iter.second;
115+
int union_value = overall_matrix - intersec_value + to_add;
116+
double IoU = (double)intersec_value / union_value;
117+
if(IoU > max_val){
118+
max_val = IoU;
119+
}
120+
}
121+
return std::pair<double,int>(max_val,overall_matrix);
122+
}

App/Components/Watershed/Watershed.cpp

+51
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,57 @@ void Watershed::foreground_mask(cv::Mat &src, cv::Mat &dst_mask, cv::Mat &foregr
167167
}
168168
dst_mask = center_mask;
169169
}
170+
//
171+
//
172+
//
173+
void Watershed::watershed_with_masks(cv::Mat &src, cv::Mat &dst, cv::Mat &background, cv::Mat &foreground, int opening_force, int blur_force, int mean_median_force, double clache_force, int med_blur_second){
174+
cv::Mat markers;
175+
cv::connectedComponents(foreground,markers);
176+
markers.setTo(1, background == 0); // Ensure background is labeled as 1
177+
markers.setTo(0, (background != 0) & (foreground == 0)); // Unknown regions are 0
178+
cv::Mat to_wat;
179+
src.copyTo(to_wat);
180+
cv::Mat image_watershed, result, watershed_markers;
181+
Transformations::opening(to_wat,to_wat,'O',opening_force);
182+
Transformations::double_blur(to_wat,to_wat,blur_force,mean_median_force);
183+
Watershed::clache(to_wat,to_wat,clache_force);
184+
cv::medianBlur(to_wat,to_wat,med_blur_second);
185+
cv::cvtColor(to_wat, image_watershed, cv::COLOR_GRAY2BGR);
186+
markers.convertTo(watershed_markers,CV_32S);
187+
cv::watershed(image_watershed,watershed_markers);
188+
watershed_markers.copyTo(dst);
189+
}
190+
//
191+
//
192+
//
193+
void Watershed::draw_watershed_lines(cv::Mat &src, cv::Mat &dst, cv::Mat &watershed_mask){
194+
cv::Mat result;
195+
cv::cvtColor(src,result,cv::COLOR_GRAY2RGB);
196+
int count_green = 0;
197+
for (int i = 0; i < watershed_mask.rows; i++) {
198+
for (int j = 0; j < watershed_mask.cols; j++) {
199+
int markerValue = watershed_mask.at<int>(i, j);
200+
if (markerValue == -1) {
201+
result.at<Vec3b>(i, j) = Vec3b((markerValue*10)%255, 105, 0); // Red for watershed boundaries
202+
}
203+
else if (markerValue == 1) {
204+
result.at<Vec3b>(i, j) = Vec3b(150, 150, 150); // Blue for background
205+
}
206+
else if (markerValue >= 2) {
207+
count_green += 1;
208+
result.at<Vec3b>(i, j) = Vec3b((markerValue*10)%255,(markerValue*10)%255,(markerValue*25)%255);
209+
}
210+
}
211+
}
212+
Mat imageBGRA;
213+
cvtColor(src, imageBGRA, COLOR_BGR2RGB);
214+
215+
// Blend the watershed result with the original image using 20% opacity for the result
216+
Mat blended;
217+
addWeighted(imageBGRA, 0.7, result, 0.3, 0, blended); // 80% original + 20% overlay
218+
blended.copyTo(dst);
219+
}
220+
170221

171222
/*
172223
***********************

App/Components/Watershed/Watershed.h

+25
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,37 @@ class Watershed{
9898

9999

100100
/**
101+
* Function performes watershed with given background mask and foreground mask
102+
* @return watershed markers
103+
* @param src
104+
* @param dst
105+
* @param background
106+
* @param foreground
107+
* @param opening_force
108+
* @param blur_force
109+
* @param mean_median_force
110+
* @param clache_force
111+
* @param med_blur_second
112+
*/
113+
114+
static void watershed_with_masks(cv::Mat &src, cv::Mat &dst, cv::Mat &background, cv::Mat &foreground, int opening_force, int blur_force, int mean_median_force, double clache_force, int med_blur_second);
115+
116+
/**
101117
* Functions uses other technique, clacheIMG and medianblur to do first preprocessing of the image
102118
* @param src source image
103119
* @param dst destination image
104120
* @param clache_limit a force of clache filter
105121
* */
106122
static void clache(cv:: Mat &src, cv::Mat &dst, double clache_limit=4.0);
123+
124+
/**
125+
* Function draws the line on the image, making it visually distinguishable
126+
* @param src
127+
* @param dst
128+
* @param watershed_mask
129+
*/
130+
static void draw_watershed_lines(cv::Mat &src, cv::Mat &dst, cv::Mat &watershed_mask);
131+
107132
//*************
108133
//** PRIVATE **
109134
//*************

App/Utils/;�%

-1
This file was deleted.

App/Utils/D�

-1
This file was deleted.

App/Utils/Options_Optimizer/main.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,15 @@ int main(int argc, char **argv){
6161
Entites::FILES::write_to_file(comm_file.c_str(),"CREATED");
6262
while(1){
6363
std::string msg = Threading::await_file_change(comm_file.c_str(),500000,2);
64-
opt->crop_save_image_sample(cropped,images,std::string(std::string(OUT_FOLDER) + std::string("/cropped_bg.jpg")));
65-
Entites::FILES::write_to_file(comm_file.c_str(),"NEW_SAMPLE",' ',false,true);
64+
std::cout << msg << std::endl;
65+
if(msg == "!NEW_SAMPLE"){
66+
opt->crop_save_image_sample(cropped,images,std::string(std::string(OUT_FOLDER) + std::string("/cropped_bg.jpg")));
67+
Entites::FILES::write_to_file(comm_file.c_str(),"$NEW_SAMPLE",' ',false,true);
68+
}
6669
}
67-
68-
//Await changes in mask file, with high limit
6970

71+
//Await changes in mask file, with high limit
7072

73+
delete opt;
7174
return 0;
7275
}

App/Utils/Options_Optimizer/main.out

31.5 KB
Binary file not shown.

App/Utils/Options_Optimizer/optimizer_temp.option App/Utils/TEST/data/algorithm_default.option

+6
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,10 @@
3838
gftt_corners<int>: 3000
3939
gftt_quality<double>: 0.05
4040
cell_radius_multiplier<int>: 2 #multiplier for cell size
41+
-WATERSHED
42+
opening_force<int>: 3
43+
blur_force<int>: 3
44+
mean_median_force<int>: 3
45+
clache_force<double>: 3.0
46+
med_blur_second<int>: 3
4147

353 KB
Binary file not shown.

App/Utils/TEST/main.cpp

+26-2
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,38 @@
44
#include "../../Components/Structures/Structures.h"
55
#include "../../Components/Optimalization/Optimaliation.h"
66
#include "../../Components/Threading/Threads.h"
7+
#include "../../Components/Structures/AlgorithmOptions.h"
78

89
/**
9-
OH BROTHER NOW I HAE TO WRITE UNIT TESTS UH
10+
This file is for testing whatever needs to be tested in that specific moment
11+
it does not contain any unit/integration tests
1012
*/
1113

1214
int main(int argc, char **argv){
15+
const std::string user_mask_path = "./data/generated_mask.bmp";
16+
const std::string image_path = "./data/cropped_bg.jpg";
17+
const std::string OPTIONS_PATH = "./data/algorithm_default.option";
1318

1419

15-
20+
Optimalization opt = Optimalization();
21+
opt.read_user_mask(user_mask_path);
22+
//Create new mask
23+
AlgorithmOptions options(OPTIONS_PATH.c_str());
24+
cv::Mat image;
25+
image = cv::imread(image_path, cv::IMREAD_GRAYSCALE);
26+
cv::Mat background_mask, foreground_mask, foreground_regions, watershed_mask, result;
27+
Watershed::background_mask(image,background_mask,options.get_int_var(0,"BACKGROUND_MASK"),options.get_int_var(1,"BACKGROUND_MASK"),options.get_db_var(2,"BACKGROUND_MASK"),options.get_int_var(3,"BACKGROUND_MASK"),options.get_int_var(4,"BACKGROUND_MASK"),options.get_int_var(5,"BACKGROUND_MASK"),options.get_db_var(6,"BACKGROUND_MASK"),options.get_int_var(7,"BACKGROUND_MASK"));
28+
// //This will be even longer
29+
Watershed::foreground_regions(image,foreground_regions,background_mask,options.get_int_var(0,"FOREGROUND_REGIONS"),options.get_int_var(1,"FOREGROUND_REGIONS"),options.get_db_var(2,"FOREGROUND_REGIONS"),options.get_int_var(3,"FOREGROUND_REGIONS"),options.get_db_var(4,"FOREGROUND_REGIONS"),options.get_int_var(5,"FOREGROUND_REGIONS"));
30+
// //And thiss will be the longest
31+
Watershed::foreground_mask(image,foreground_mask,foreground_regions,background_mask,options.get_int_var(0,"FOREGROUND_MASK"), options.get_int_var(1,"FOREGROUND_MASK"), options.get_int_var(2,"FOREGROUND_MASK"), options.get_int_var(3,"FOREGROUND_MASK"), options.get_db_var(4,"FOREGROUND_MASK"), options.get_db_var(5,"FOREGROUND_MASK"),options.get_db_var(6,"FOREGROUND_MASK"), options.get_db_var(7,"FOREGROUND_MASK"), options.get_int_var(8,"FOREGROUND_MASK"), options.get_int_var(9,"FOREGROUND_MASK"), options.get_int_var(10,"FOREGROUND_MASK"), options.get_db_var(11,"FOREGROUND_MASK"), options.get_int_var(12,"FOREGROUND_MASK"));
32+
//And apply the watershed with masks and options
33+
Watershed::watershed_with_masks(image,watershed_mask,background_mask,foreground_mask,options.get_int_var(0,"WATERSHED"),options.get_int_var(1,"WATERSHED"),options.get_int_var(2,"WATERSHED"),options.get_db_var(3,"WATERSHED"),options.get_int_var(4,"WATERSHED"));
34+
//new sample should be in variable watershed_mask
35+
Watershed::draw_watershed_lines(image,result,watershed_mask);
36+
// std::cout << "Oversection_over_union: ";
37+
std::cout << opt.calculate_intersection_over_union(watershed_mask) << std::endl;
38+
// cv::imshow("SHOW",result);
39+
// cv::waitKey(0);
1640
return 0;
1741
}

App/Utils/TEST/run.sh

100644100755
File mode changed.

App/Utils/algorithm_default.option

+6
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,10 @@
3838
gftt_corners<int>: 3000
3939
gftt_quality<double>: 0.05
4040
cell_radius_multiplier<int>: 2 #multiplier for cell size
41+
-WATERSHED
42+
opening_force<int>: 3
43+
blur_force<int>: 3
44+
mean_median_force<int>: 3
45+
clache_force<double>: 3.0
46+
med_blur_second<int>: 3
4147

App/Utils/z�

-1
This file was deleted.

App/Utils/ȱ

-1
This file was deleted.

App/Utils/�N

-1
This file was deleted.

App/Utils/�S

-1
This file was deleted.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)