Blame view

src/mask_manipulation.h 8.69 KB
5f3cba02   David Mayerich   initial public co...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  #ifndef MASK_MANIPULATION
  #define MASK_MANIPULATION
  
  #include <numeric>
  
  #include <stim/image/image.h>
  
  ///This file contains functions designed to process, subsample, and manipulate masks images
  
  void push_training_image(std::vector< stim::image<unsigned char> >& C, std::vector<size_t>& nP, size_t& tP, stim::image<unsigned char> I) {
  	C.push_back(I);
  	size_t npixels = I.nnz();
  	nP.push_back(npixels);									//push the number of pixels onto the pixel array
  	tP += npixels;											//add to the running total of pixels
  //	nC++;
  }
  
  //re-calculates the total number of pixels in an image list
  size_t calc_tP(std::vector< stim::image<unsigned char> >& C, std::vector< size_t > nP) {
  	size_t tP = 0;
  	for (size_t i = 0; i < C.size(); i++) {
  		tP += nP[i];
  	}
  	return tP;
  }
  
  size_t calc_tP(std::vector< stim::image<unsigned char> >& C) {
  	std::vector<size_t> nP(C.size());
  	for (size_t ci = 0; ci < C.size(); ci++)
  		nP[ci] = C[ci].nnz();
  	return calc_tP(C, nP);
  }
  
  //load a mask given a mask filename
  void AND_mask(std::vector< stim::image<unsigned char> >& C, std::vector<size_t>& nP, size_t& tP, std::string maskfile){
  	stim::image<unsigned char> mask_image(maskfile);				//load the image
  	stim::image<unsigned char> mono_image = mask_image.channel(0);	//retrieve the first channel
  
  	if(C.size() == 0){
  		push_training_image(C, nP, tP, mono_image);
  	}
  	else{
  		size_t N = C[0].width() * C[0].height();									//precompute the number of pixels in the mask
  		tP = 0;
  		for(size_t xy = 0; xy < N; xy++){					//for each pixel in the mask
  			if(!C.back().data()[xy] || !mono_image.data()[xy]){			//if both the current and new mask is true
  				C.back().data()[xy] = 0;
  			}
  		}
  		nP.back() = C.back().nnz();
  	}
  	calc_tP(C, nP);	
  	std::cout<<"masked pixels: "<<tP<<std::endl;
  }
  
  void push_full_mask(std::vector< stim::image< unsigned char > >& C, size_t X, size_t Y) {
  	stim::image<unsigned char> img(X, Y, 1);				//create an empty image
  	img = 255;												//set all values to 255
  
  	C.push_back(img);											//push the image onto the image array
  }
  
  void push_fully_masked_image(std::vector< stim::image< unsigned char > >& C, std::vector<size_t>& nP, size_t& tP, size_t X, size_t Y){
  	
  	push_full_mask(C, X, Y);
  
  	size_t npixels = C[C.size() - 1].nnz();
  	nP.push_back(npixels);									//push the number of pixels onto the pixel array
  	tP += npixels;											//add to the running total of pixels
  	//nC++;													//increment the number of classes
  }
  
  void subsample_masks(std::vector< stim::image<unsigned char> >&I, size_t n) {
  	size_t tP = calc_tP(I);
  	if (tP <= n) return;										//if there are less than n pixels already, nothing changes
  
  	std::vector< stim::image<unsigned char> > new_C;		//create a new training image array
  	new_C.resize(I.size());										//set the correct size of the array
  
  	std::vector<size_t> idx(tP);							//create an array that will store the indices of each pixel
  	size_t ip = 0;											//index into the index array
  
  	size_t X = I[0].width();
  	size_t Y = I[0].height();
  
  	for (size_t c = 0; c < I.size(); c++) {							//for each training image
  		new_C[c] = stim::image<unsigned char>(X, Y, 1);		//create a new image that matches the size of the original
  		new_C[c] = 0;										//set all values in the image to zero
  		std::vector<size_t> c_idx = I[c].sparse_idx();		//get the indices of each non-zero pixel
  
  		for (size_t i = 0; i < c_idx.size(); i++) {			//for each index
  			idx[ip + i] = c_idx[i] + c * X * Y;				//scale the index by the image number and store it in the index array
  		}
  		ip += c_idx.size();									//increment the global index
  	}
  
  	std::random_device rd;											//create a random number generation function
  	std::mt19937 g(rd());
  	std::shuffle(idx.begin(), idx.end(), g);						//shuffle the index values
  
  	size_t c, ix;
  	for (size_t i = 0; i < n; i++) {									//for each of the first n random pixels
  		c = idx[i] / (X * Y);										//calculate the image that the current pixel is in
  		ix = idx[i] - c * X * Y;									//calculate the 1D index of the current pixel
  		new_C[c].data()[ix] = I[c].data()[ix];						//store the value of the pixel in the new C array
  	}
  
  	I = new_C;														//replace the class image array
  }
  
  /// This function picks a random set of N samples from among the training masks
  void set_random_samples(std::vector< stim::image<unsigned char> >& C, std::vector<size_t>& nP, size_t& tP, size_t n){
  
  	subsample_masks(C, n);
  
  	tP = 0;															//reset the total number of pixels to zero
  	for(size_t c = 0; c < C.size(); c++){									//for each class image
  		nP[c] = C[c].nnz();											//re-calculate the number of pixels in each image
  		tP += nP[c];												//re-calculate the total number of pixels
  	}
  }
  
  /// Calculate a new image composed of N random pixels from the input image
  stim::image<unsigned char> random_subset(stim::image<unsigned char> in, size_t n) {
  	if (n >= in.nnz()) return in;									//if the number of pixels requested is less than the number of masked pixels, return the original image
  	std::vector<size_t> idx = in.sparse_idx();					//get the indices of each non-zero pixel
  	std::random_device rd;											//create a random number generation function
  	std::mt19937 g(rd());
  	std::shuffle(idx.begin(), idx.end(), g);					//shuffle the index values
  
  	stim::image<unsigned char> out(in.width(), in.height(), 1);		//create an output image the same size as the input image
  	out = 0;														//set all pixels to zero
  
  	for (size_t i = 0; i < n; i++)								//go through each pixel index until the number of requested pixels is reached
  		out.data()[idx[i]] = 255;								//set the pixel value at that location to TRUE
  
  	return out;													//return the output image
  }
  
  
  /// This function picks a random set of N samples from among the training masks. The samples are selected to make the training
  ///	set as balanced as possible (equal numbers of training samples from each class).
  void set_random_samples_balanced(std::vector< stim::image< unsigned char > >& C, size_t n, std::string output_mask) {
  
  	//size_t tP = calc_tP(C, nP);
  	//if (tP <= n) return;									//if there are less than n pixels already, nothing changes
  
  	std::vector< stim::image<unsigned char> > new_C;		//create a new training image array
  	new_C.resize(C.size());										//set the correct size of the array
  
  	//calculate the number of pixels in each training image
  	std::vector<size_t> num_pix_per_class(C.size());				//create a vector to store the number of pixels in each class
  	for (size_t c = 0; c < C.size(); c++)							//for each class
  		num_pix_per_class[c] = C[c].nnz();					//calculate the number of pixels
  
  	std::vector<int> c_idx(num_pix_per_class.size());		//create a vector that will store indices into the sorted class array
  	std::iota(c_idx.begin(), c_idx.end(), 0);				//fill the index array with a list of indices [0, c)
  
  	//create a function that compares two values in the num_pix_per_class array and sorts the indices
  	auto comparator = [&num_pix_per_class](int a, int b) { return num_pix_per_class[a] < num_pix_per_class[b]; };
  	std::sort(c_idx.begin(), c_idx.end(), comparator);		//sort the indices, which can be used to look up the sorted version of the number of pixels per class
  
  	size_t ppc = n / C.size();									//calculate the ideal number of pixels per class
  	size_t pixels_used = 0;									//number of pixels that have already been accepted into the mask array
  
  	for (size_t ci = 0; ci < C.size(); ci++) {					//for each class (starting with the smallest)
  		ppc = (n - pixels_used) / (C.size() - ci);				//determine the number of pixels per class that have to be used to balance out the rest of the classes
  		size_t pix = C[c_idx[ci]].nnz();					//get the number of pixels in this class
  		new_C[c_idx[ci]] = random_subset(C[c_idx[ci]], ppc);	//get a random subset of the class
  		pixels_used += new_C[c_idx[ci]].nnz();				//get the actual number of pixels in the subsample and use it to increment the number of pixels used in the new mask images			
  	}
  
  	C = new_C;														//replace the class image array
  
  	if (output_mask != "") {											//if the user specifies a mask filename, save each subsampled image
  		stim::filename fmask = output_mask;							//create a file name object from the mask string
  		for (size_t c = 0; c < C.size(); c++) {
  			stim::filename f = fmask.insert(c, 4);					//generate a numbered file name
  			C[c].save(f.str());										//convert the file name to a string and save the image
  		}
  	}
  
  	/*//tP = 0;															//reset the total number of pixels to zero
  	for (size_t c = 0; c < C.size(); c++) {								//for each class image
  		//nP[c] = C[c].nnz();											//re-calculate the number of pixels in each image
  		//tP += nP[c];												//re-calculate the total number of pixels
  		if (maskfile != "") {
  			std::stringstream ss;
  			ss << "resample_class_" << c << ".bmp";
  			C[c].save(ss.str());
  		}
  	}*/
  }
  
  
  #endif