-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSimpleFlowDenseOF.cpp
286 lines (237 loc) · 12.8 KB
/
SimpleFlowDenseOF.cpp
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
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
#include "SimpleFlowDenseOF.h"
// POTENTIAL OPTIMISATIONS:
// * move calculation of offets elsewhere as it only needs to be calculated once.
// * check to see if handling out of bounds for norms can be done fasters.
// * check to see if norms can handled better then using pow... perhaps images of shorts is better than ints.
// * table for sigma_c calculations.. with and without interpolation.
bool SimpleFlowDenseOF::calcFlow( Mat &resultImage ){
assert(numImages == 2 && (resultImage.type() == CV_32FC2));
// first build image pyramids
vector<Mat> image0Pyramid;
vector<Mat> image1Pyramid;
buildPyramid( (*images)[0], image0Pyramid, numPyramidLevels );
buildPyramid( (*images)[1], image1Pyramid, numPyramidLevels );
Mat currentFlow[numPyramidLevels+1];
Mat newFlow[numPyramidLevels+1];
// create Mats for the current level solution
for (unsigned ii = 0; ii<=numPyramidLevels; ii++) {
currentFlow[ii] = Mat( image0Pyramid[ii].rows, image0Pyramid[ii].cols, CV_16SC2 );
newFlow[ii] = Mat( image0Pyramid[ii].rows, image0Pyramid[ii].cols, CV_16SC2 );
}
createMapping();
// run flow at top (smallest) pyramid level
currentFlow[numPyramidLevels] = Mat::zeros(currentFlow[numPyramidLevels].rows, currentFlow[numPyramidLevels].cols, CV_16SC2 ); // init top level to zero
calcFlowAtLevel( image0Pyramid[numPyramidLevels], image1Pyramid[numPyramidLevels], currentFlow[numPyramidLevels], newFlow[numPyramidLevels] );
for (int ii = numPyramidLevels-1; ii>=0; ii--) {
newFlow[ii+1] = 2*newFlow[ii+1]; // multiply by two to prepare for new image scaling
pyrUp( currentFlow[ii+1], currentFlow[ii], Size(currentFlow[ii+1].cols*2, currentFlow[ii+1].rows*2 ) );
calcFlowAtLevel( image0Pyramid[ii], image1Pyramid[ii], currentFlow[ii], newFlow[ii] );
}
// compute final full resolution flow
//calcFlowAtLevel( image0Pyramid[0], image1Pyramid[0], currentFlow, resultImage );
return 1;
}
bool SimpleFlowDenseOF::calcFlowAtLevel( Mat &fromImage, Mat &toImage, Mat ¤tFlow, Mat &newFlow ){
bool successFlag;
assert( fromImage.rows == toImage.rows );
assert( fromImage.cols == toImage.cols );
assert( fromImage.rows == currentFlow.rows );
assert( fromImage.cols == currentFlow.cols );
assert( fromImage.rows == newFlow.rows );
assert( fromImage.cols == newFlow.cols );
assert( fromImage.elemSize() == toImage.elemSize() );
//// first calculate the rgb norms
// setup required mats
Mat norms(fromImage.rows, fromImage.cols, CV_32FC(OmegaPixelCount));
// go ahead a calculate means
successFlag = calcNormsWithMean( fromImage, toImage, currentFlow, norms ); assert(successFlag);
// vector<Mat> splitImages;
// split(norms, splitImages);
// for (unsigned kk=0; kk<OmegaPixelCount; kk++) {
// imshow( "norm images", splitImages[kk]);
// waitKey();
// }
successFlag = filterThenFindEnergyMinimiser( fromImage, norms, currentFlow, newFlow ); assert(successFlag);
return successFlag;
}
bool SimpleFlowDenseOF::calcNormsWithMean( Mat &fromImage, Mat &toImage, Mat ¤tFlow, Mat &norms ){
assert( fromImage.rows == norms.rows );
assert( fromImage.cols == norms.cols );
assert( fromImage.isContinuous() && toImage.isContinuous() && norms.isContinuous() );
// build offset vec (prob should do somewhere else)
int OmegaRadiusInt = (int) OmegaRadius;
int offsets1D[OmegaPixelCount];
unsigned posCount = 0;
for (int ii=-OmegaRadiusInt; ii<=OmegaRadiusInt; ii++) {
for (int jj=-OmegaRadiusInt; jj<=OmegaRadiusInt; jj++) {
offsets1D[posCount] = ii*toImage.step + jj*toImage.channels();
posCount++;
}
}
assert(posCount == OmegaPixelCount);
// go through all pixels of fromImage, determine norms
for (int ii=0; ii<fromImage.rows; ii++) {
const uchar* fImgRowPtr = fromImage.ptr<uchar>(ii);
float* normRowPtr = norms.ptr<float>(ii);
const short* currentFlowRowPtr = currentFlow.ptr<short>(ii);
for (int jj=0; jj<fromImage.cols; jj++) {
const uchar* fImgPix = fImgRowPtr + fromImage.channels()*jj; // get fromImage pixel
float* normPix = normRowPtr + norms.channels()*jj; // get norm pixel
const short* currentFlowPix = currentFlowRowPtr + currentFlow.channels()*jj; // get currentFlow displacement
int iiOm = ii + currentFlowPix[0]; // get centre of Omega
int jjOm = jj + currentFlowPix[1];
uchar* tImgCentrePix = toImage.ptr<uchar>(iiOm) + jjOm*toImage.channels(); // get pixel at centre of Omega
if( (iiOm > OmegaRadiusInt) && (iiOm < toImage.rows-OmegaRadiusInt) && // check if test support may fall outside toImage
(jjOm > OmegaRadiusInt) && (jjOm < toImage.cols-OmegaRadiusInt) )
{ // no bounds checks required here
for (unsigned aa=0; aa<OmegaPixelCount; aa++) {
*normPix = 0;
uchar* tImgPix = tImgCentrePix + offsets1D[aa];
for (int bb=0; bb<fromImage.channels(); bb++) {
*normPix += pow((float)(*(fImgPix+bb) - *(tImgPix+bb)), 2);
}
normPix++;
}
} else { // bounds checks required here
for (unsigned aa=0; aa<OmegaPixelCount; aa++) {
int iiPix = iiOm + vecyOmega[aa];
if (iiPix < 0 || iiPix >= toImage.rows){ // if outside image vertically, set to FLT_MAX
*normPix = FLT_MAX;
} else {
int jjPix = jjOm + vecxOmega[aa];
if (jjPix < 0 || jjPix >= toImage.cols){ // if outside image horizontally, set to FLT_MAX
*normPix = FLT_MAX;
} else { // else calc norm
*normPix = 0;
uchar* tImgPix = tImgCentrePix + offsets1D[aa];
for (int bb=0; bb<fromImage.channels(); bb++) {
*normPix += pow((float)(*(fImgPix+bb) - *(tImgPix+bb)), 2);
}
}
}
normPix++;
}
}
}
}
return 1;
}
bool SimpleFlowDenseOF::filterThenFindEnergyMinimiser( Mat &fromImage, Mat &norms, Mat ¤tFlow, Mat &newFlow ){
assert( fromImage.rows == norms.rows );
assert( fromImage.cols == norms.cols );
assert( fromImage.rows == currentFlow.rows );
assert( fromImage.cols == currentFlow.cols );
assert( fromImage.rows == newFlow.rows );
assert( fromImage.cols == newFlow.cols );
assert( currentFlow.elemSize() == newFlow.elemSize() );
// calculate Mat containing pixel displacement factor
int etaRadiusInt = (int) etaRadius;
float dispFact[etaPixelCount];
int dispOffset[etaPixelCount];
unsigned posCount=0;
for (int ii = -etaRadiusInt; ii<=etaRadiusInt; ii++) {
for (int jj = -etaRadiusInt; jj<=etaRadiusInt; jj++) {
dispFact[posCount] = exp(-(ii*ii + jj*jj)/(2*sigma_d));
dispOffset[posCount] = ii*fromImage.step + jj*fromImage.channels();
posCount++;
}
}
assert(posCount == etaPixelCount);
// go through all pixels of levelResult, set pixel to direction which minimises energy
for (int ii=0; ii<newFlow.rows; ii++) {
const uchar* fImgRowPtr = fromImage.ptr<uchar>(ii);
const short* currentFlowRowPtr = currentFlow.ptr<short>(ii);
short* newFlowRowPtr = newFlow.ptr<short>(ii);
for (int jj=0; jj<newFlow.cols; jj++) {
const uchar* fImgPix = fImgRowPtr + jj*fromImage.channels(); // get fromImage pixel
// perform a bilateral filter for each candidate vector over neighbouring pixels to (ii,jj), ie all pixels in Eta
// first calculate colour difference factor for all pixels in eta, and also check bounds and create new list
float incTotFact[etaPixelCount]; // total bilateral factor (disp + colourFact) for included pixels
int etaPosy[etaPixelCount];
int etaPosx[etaPixelCount];
unsigned incPosCount = 0; // included pixels increment
for (unsigned aa=0; aa<etaPixelCount; aa++) {
int testPosy = ii+vecyeta[aa];
int testPosx = jj+vecxeta[aa];
if ( (testPosy >= 0) && (testPosy < fromImage.rows) &&
(testPosx >= 0) && (testPosx < fromImage.cols) ) {
etaPosy[incPosCount] = testPosy;
etaPosx[incPosCount] = testPosx;
const uchar* fImgOtherPix = fImgPix + dispOffset[aa];
float colDiffNorm2 = 0;
for (int bb = 0; bb<fromImage.channels(); bb++ ) {
float diff = (float)(*(fImgPix+bb) - *(fImgOtherPix+bb));
colDiffNorm2 += diff*diff;
}
incTotFact[incPosCount] = dispFact[aa] * exp(-colDiffNorm2/(2*sigma_c));
incPosCount++;
}
}
assert(incPosCount <= etaPixelCount);
const short* currentFlowPix = currentFlowRowPtr + jj*currentFlow.channels(); // get currentFlow pixel
short* currentFlowOtherPixTop = (short*)currentFlow.data;
int* mapto1DOmegaTop = (int*)mapto1DOmega.data;
float* normsTop = (float*)norms.data;
// now, need to go through each candidate vector in Omega, and assign an energy (perhaps should normalise for vectors with less contributions (at boundaries))
float bilateralSumMin = FLT_MAX;
int winnerVectorIndex = -1;
for (unsigned Omega_i; Omega_i<OmegaPixelCount; Omega_i++) {
float bilateralSum = 0;
for (unsigned eta_i; eta_i<incPosCount; eta_i++) {
// need to determine normsOtherPix
short* currentFlowOtherPix = currentFlowOtherPixTop + etaPosy[eta_i]*currentFlow.step + etaPosx[eta_i]*currentFlow.channels();
int correctedOffsety = *currentFlowPix - *currentFlowOtherPix;
int correctedOffsetx = *(currentFlowPix+1) - *(currentFlowOtherPix+1);
if( (abs(correctedOffsety) > (int)OmegaRadius) || (abs(correctedOffsetx) > (int)OmegaRadius) ) // if outside Omega support, no contribution
continue;
int requiredNormsChannel = *(mapto1DOmegaTop + (correctedOffsety+OmegaRadius)*mapto1DOmega.step +
(correctedOffsetx+OmegaRadius));
float normsVal = *(normsTop + etaPosy[eta_i]*norms.step + etaPosx[eta_i]*norms.channels() + requiredNormsChannel);
bilateralSum += normsVal*incTotFact[eta_i];
if( bilateralSum < bilateralSumMin ) {
bilateralSumMin = bilateralSum;
winnerVectorIndex = Omega_i;
}
}
}
short* newFlowPix = newFlowRowPtr + jj*newFlow.channels(); // get newFlow pixel
*(newFlowPix ) = *(currentFlowPix ) + vecyOmega[winnerVectorIndex]; // set value to current approximation, plus best candidate from omega;
*(newFlowPix+1) = *(currentFlowPix+1) + vecxOmega[winnerVectorIndex];
}
}
return 1;
}
void SimpleFlowDenseOF::createMapping(){
// create mapping for omega support
mapto1DOmega = Mat(2*OmegaRadius+1, 2*OmegaRadius+1, CV_32SC1);
int OmegaRadiusInt = (int)OmegaRadius;
unsigned posCount = 0;
for (int aa = -OmegaRadiusInt; aa<=OmegaRadiusInt; aa++) {
for (int bb = -OmegaRadiusInt; bb<=OmegaRadiusInt; bb++) {
vecyOmega.push_back(aa);
vecxOmega.push_back(bb);
mapto1DOmega.at<int>(aa+OmegaRadius,bb+OmegaRadius) = posCount;
posCount++;
}
}
assert(posCount == (2*OmegaRadius+1)*(2*OmegaRadius+1));
// create mapping for eta basis
mapto1Deta = Mat(2*etaRadius+1, 2*etaRadius+1, CV_32SC1);
int etaRadiusInt = (int)etaRadius;
posCount = 0;
for (int aa = -etaRadiusInt; aa<=etaRadiusInt; aa++) {
for (int bb = -etaRadiusInt; bb<=etaRadiusInt; bb++) {
vecyeta.push_back(aa);
vecxeta.push_back(bb);
mapto1Deta.at<int>(aa+etaRadius,bb+etaRadius) = posCount;
posCount++;
}
}
assert(posCount == (2*etaRadius+1)*(2*etaRadius+1));
}