utility.h 10.5 KB
Newer Older
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
1
using namespace cv;
2
using namespace std;
3
namespace fs = std::filesystem;
4
5


Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
6
7
8
9
10
11
12
13
14
15
/*************************************************************************************************/
/**************************************** TIME FUNCTIONS *****************************************/
/*************************************************************************************************/

/**
 * @brief Convert an int representing milliseconds to the corresponding Time Label string.
 *
 * @param ms the number of milliseconds.
 * @return string the corresponding Time Label string.
 */
16
17
string getTimeLabel(int ms) {

18
19
20
21
22
23
	int mil = ms % 1000;
	int sec = ms / 1000;
	int min = (sec / 60) % 60;
	int hours = sec / 3600;
	sec = sec % 60;

24
	string hoursStr = to_string(hours), minStr = to_string(min), secStr = to_string(sec), milStr = to_string(mil);
25
26
27
28
29
30
31
32
33
34
35
36
37
	if (hours < 10)
		hoursStr = "0" + hoursStr;
	if (min < 10)
		minStr = "0" + minStr;
	if (sec < 10)
		secStr = "0" + secStr;
	if (mil < 100) {
		if (mil < 10) {
			milStr = "00" + milStr;
		} else {
			milStr = "0" + milStr;
		}
	}
38
39

	string timeLabel = hoursStr + ":" + minStr + ":" + secStr + "." + milStr;
40
41
42
43
44

	return timeLabel;

}

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
45
46
47
48
49
50
51
52

/**
 * @brief Convert an int representing milliseconds to the corresponding Time Label string, but with dashes '-' charachers instead of periods '.' and colons ':'.
 * Useful for file names.
 *
 * @param ms the number of milliseconds.
 * @return string the corresponding Time Label string.
 */
53
54
string getSafeTimeLabel(int ms) {

55
56
57
58
59
60
	int mil = ms % 1000;
	int sec = ms / 1000;
	int min = (sec / 60) % 60;
	int hours = sec / 3600;
	sec = sec % 60;

61
	string hoursStr = to_string(hours), minStr = to_string(min), secStr = to_string(sec), milStr = to_string(mil);
62
63
64
65
66
67
68
69
70
71
72
73
74
75
	if (hours < 10)
		hoursStr = "0" + hoursStr;
	if (min < 10)
		minStr = "0" + minStr;
	if (sec < 10)
		secStr = "0" + secStr;
	if (mil < 100) {
		if (mil < 10) {
			milStr = "00" + milStr;
		} else {
			milStr = "0" + milStr;
		}
	}

76
	string timeLabel = hoursStr + "-" + minStr + "-" + secStr + "-" + milStr;
77

78
	return timeLabel;
79
80
81
82

}


Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
83
84
85
/*************************************************************************************************/
/*************************************** PARSING FUNCTIONS ***************************************/
/*************************************************************************************************/
86

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
87
88
89
90
91
92
93
94

/**
 * @brief Separates video file name from its extension.
 *
 * @param[in] path Full video path;
 * @param[out] fileName Video file name;
 * @param[out] extension Video extension.
 */
95
96
97
98
99
void findFileNameFromPath(string* path, string* fileName, string* extension) {
	*path = path->substr(path->find_last_of("'") + 1, path->size());
	string path_without_extension = path->substr(0, path->find_last_of("."));
	*fileName = path_without_extension.substr(path_without_extension.find_last_of("/") + 1, path_without_extension.size());
	*extension = path->substr(path->find_last_of(".") + 1, path->size());
100
101
}

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
102
103
104
105
106
107
108
109
110

/**
 * @brief Check if the specified input video file exists and is supported.
 *
 * @param[in] videoPath Full video path;
 * @param[out] fileName Video file name;
 * @param[out] extension Video extension.
 * @return int -1 if the format is not supported, 0 otherwise.
 */
111
int findFileName(string videoPath, string &fileName, string &extension) {
112
113
114
115

    findFileNameFromPath(&videoPath, &fileName, &extension);

    if (extension.compare("avi") != 0 && extension.compare("mp4") != 0 && extension.compare("mov") != 0) {
116
        cerr << "Input file extension must be \"avi\", \"mp4\" or \"mov\"." << endl;
117
118
        return -1;
    } else {
119
120
121
        cout << "Video to be analysed: " << endl;
        cout << "    File name:  " << fileName << endl;
        cout << "    Extension:  " << extension << endl;
122
123
124
125
126
    }
    return 0;
}


Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*************************************************************************************************/
/*************************************** OpenCV FUNCTIONS ****************************************/
/*************************************************************************************************/

/**
 * @brief Detects a given shape in an image, using a the OpenCV algorithm GeneralizedHoughGuil.
 *
 * @param[in] alg the algorithm instance;
 * @param[in] templateShape the shape to detect;
 * @param[in] posThresh the position votes threshold;
 * @param[out] positivePositions vector representing the position assigned to each found rectangle for positive angles;
 * @param[out] positiveVotes vector representing the vote assigned to each found rectangle for positive angles;
 * @param[out] negativePositions vector representing the position assigned to each found rectangle for negative angles;
 * @param[out] negativeVotes vector representing the vote assigned to each found rectangle for negative angles;
 * @param[in] processingArea the image to be processed.
 */
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
143
void detectShape(Ptr<GeneralizedHoughGuil> alg, Mat templateShape, int posThresh, vector<Vec4f> &positivePositions, Mat &positiveVotes, vector<Vec4f> &negativePositions, Mat &negativeVotes, Mat processingArea) {
144

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
145
146
147
148
149
150
151
152
153
	alg -> setPosThresh(posThresh);
	alg -> setTemplate(templateShape);

	int oldSizePositive = 0;
	int i = 0;
	int maxVote = 0;

	// Process shapes with positive angles
	alg -> setMinAngle(0);
154
	alg -> setMaxAngle(3);
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
155
156
157
158
159
160
161
162
163
164
165
166
	while (true) {
		alg -> detect(processingArea, positivePositions, positiveVotes);
		int currentSize = positivePositions.size();
		if (currentSize == 1) {
			// We detected the most interesting shape
			break;
		} else if (currentSize == 0 && oldSizePositive > 0) {
			// It is not possible to detect only one shape with the current parameters
			alg -> setPosThresh(posThresh+i-1); // Decrease position value
			alg -> detect(processingArea, positivePositions, positiveVotes); // Detect all available shapes
			break;
		} else if (currentSize == 0 && oldSizePositive == 0) {
167
			// Impossible to find with these parameters
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
			break;
		}
		oldSizePositive = currentSize;
		// Find maximum vote
		for (int j = 0; j < positiveVotes.cols / 3; j++) {
			if (positiveVotes.at<int>(3*j) > maxVote)
				maxVote = positiveVotes.at<int>(3*j);
		}

		if (currentSize > 10) {
			i += 5; // To speed up computation when there are too many matches
		} else if (maxVote - (posThresh + i) > 100) {
			i += 100; // To speed up computation when there are few super high matches
		} else {
			i++;
		}
		alg -> setPosThresh(posThresh+i);
	}

	int oldSizeNegative = 0;
	// Reset incremental position value
	i = 0;
	maxVote = 0;
	// Process shapes with negative angles
192
	alg -> setMinAngle(357);
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
	alg -> setMaxAngle(360);
	while (true) {
		alg -> detect(processingArea, negativePositions, negativeVotes);
		int currentSize = negativePositions.size();
		if (currentSize == 1) {
			// We detected the most interesting shape
			break;
		} else if (currentSize == 0 && oldSizeNegative > 0) {
			// It is not possible to detect only one shape with the current parameters
			alg -> setPosThresh(posThresh+i-1); // Decrease position value
			alg -> detect(processingArea, negativePositions, negativeVotes); // Detect all available shapes
			break;
		} else if (currentSize == 0 && oldSizeNegative == 0) {
			// Impossible to found with these parameters
			break;
		}
		oldSizeNegative = currentSize;

		// Find maximum vote
		for (int j = 0; j < positiveVotes.cols / 3; j++) {
			if (positiveVotes.at<int>(3*j) > maxVote)
				maxVote = positiveVotes.at<int>(3*j);
		}

		if (currentSize > 10) {
			i += 5; // To speed up computation when there are too many matches
		} else if (maxVote - (posThresh + i) > 100) {
			i += 100; // To speed up computation when there are few super high matches
		} else {
			i++;
		}
		alg -> setPosThresh(posThresh+i);
	}
}

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241

/**
 * @brief Draw rectangles on an image.
 *
 * @param frame Frame on which the rectangles will be drawn;
 * @param positions The position of the rectangle;
 * @param color The color of the rectangle;
 * @param width The width of the rectangle;
 * @param height The height of the rectangle;
 * @param offsetX X offset on the position of the rectangle;
 * @param offsetY Y offset on the position of the rectangle;
 * @param processingScale Scaling factor, useful for downsizing.
 * @return RotatedRect Object representing the drawn rectangle.
 */
242
RotatedRect drawShapes(Mat frame, Vec4f &positions, Scalar color, int width, int height, int offsetX, int offsetY, float processingScale) {
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
243
244
245
246

	RotatedRect rr;
	Point2f rrpts[4];

247
248
249
	Point2f pos(positions[0]+offsetX, positions[1]+offsetY);
	float scale = positions[2];
	float angle = positions[3];
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
250

251
252
253
	rr.center = pos * processingScale;
	rr.size = Size2f(width * scale * processingScale, height * scale * processingScale);
	rr.angle = angle;
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
254

255
	rr.points(rrpts);
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
256

257
258
259
260
	line(frame, rrpts[0], rrpts[1], color, 2);
	line(frame, rrpts[1], rrpts[2], color, 2);
	line(frame, rrpts[2], rrpts[3], color, 2);
	line(frame, rrpts[3], rrpts[0], color, 2);
Nadir Dalla Pozza's avatar
Update.    
Nadir Dalla Pozza committed
261
262

	return rr;
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
263
264
}

265

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
266
267
268
269
270
271
272
273
274
275
276
/**
 * @brief Function to deinterlace the current image.
 *
 * @param[in] frame image to be processed;
 * @param[out] odd_frame odd plane;
 * @param[out] even_frame even plane.
 */
void separateFrame(cv::Mat frame, cv::Mat &odd_frame, cv::Mat &even_frame) {

	int i_odd_frame = 0;
	int i_even_frame = 0;
277
278
279
280

	for (int i = 0; i < frame.rows; i++) {
		for (int j = 0; j < frame.cols; j++) {
			if (i % 2 == 0) {
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
281
282
283
				even_frame.at<cv::Vec3b>( i_even_frame, j )[0] = frame.at<cv::Vec3b>(i, j)[0];
				even_frame.at<cv::Vec3b>( i_even_frame, j )[1] = frame.at<cv::Vec3b>(i, j)[1];
				even_frame.at<cv::Vec3b>( i_even_frame, j )[2] = frame.at<cv::Vec3b>(i, j)[2];
284
			} else {
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
285
286
287
				odd_frame.at<cv::Vec3b>( i_odd_frame, j )[0] = frame.at<cv::Vec3b>(i, j)[0];
				odd_frame.at<cv::Vec3b>( i_odd_frame, j )[1] = frame.at<cv::Vec3b>(i, j)[1];
				odd_frame.at<cv::Vec3b>( i_odd_frame, j )[2] = frame.at<cv::Vec3b>(i, j)[2];
288
289
290
291
			}
		}

		if (i % 2 == 0) {
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
292
			i_even_frame++;
293
		} else {
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
294
			i_odd_frame++;
295
296
297
298
299
300
301
		}
	}

	return;

}

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
302
303
304
305
306
307
308
309

/**
 * @brief Compute the number of different pixels between two frames.
 *
 * @param prevFrame the first frame;
 * @param currentFrame the second frame.
 * @return cv::Mat A black and white frame, where black pixels represent a difference, while white pixels represent an equality.
 */
310
311
312
313
314
315
316
317
318
319
320
321
322
323
cv::Mat difference(cv::Mat &prevFrame, cv::Mat &currentFrame) {
	cv::Mat diff = currentFrame.clone();
	for (int i = 0; i < currentFrame.rows; i++) {
		for (int j = 0; j < currentFrame.cols; j++) {
			if (prevFrame.at<cv::Vec3b>(i, j)[0] != currentFrame.at<cv::Vec3b>(i, j)[0] || prevFrame.at<cv::Vec3b>(i, j)[1] != currentFrame.at<cv::Vec3b>(i, j)[1] || prevFrame.at<cv::Vec3b>(i, j)[2] != currentFrame.at<cv::Vec3b>(i, j)[2]) {
				// Different pixels
				diff.at<cv::Vec3b>(i, j)[0] = 0;
			} else {
				// Identical pixels
				diff.at<cv::Vec3b>(i, j)[0] = 255;
			}
		}
	}
	return diff;
324
}