Skip to content

Commit 6f820b0

Browse files
committed
Merge branch 4.x
2 parents 845eb15 + 8402454 commit 6f820b0

File tree

17 files changed

+1448
-329
lines changed

17 files changed

+1448
-329
lines changed

modules/ccalib/tutorials/multi_camera_tutorial.markdown

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This tutorial will show how to use the multiple camera calibration toolbox. This
55

66
Random Pattern Calibration Object
77
-------------------------------
8-
The random pattern is an image that is randomly generated. It is "random" so that it has many feature points. After generating it, one print it out and use it as a calibration object. The following two images are random pattern and a photo taken for it.
8+
The random pattern is an image that is randomly generated. It is "random" so that it has many feature points. After generating it, print it out and use it as a calibration object. The following two images are random pattern and a photo taken for it.
99

1010
![image](img/random_pattern.jpg)
1111
![image](img/pattern_img.jpg)
@@ -14,7 +14,7 @@ To generate a random pattern, use the class ```cv::randpattern::RandomPatternGen
1414
```
1515
cv::randpattern::RandomPatternGenerator generator(width, height);
1616
generator.generatePattern();
17-
pattern = generator.getPattern();
17+
cv::Mat pattern = generator.getPattern();
1818
```
1919
Here ```width``` and ```height``` are width and height of pattern image. After getting the pattern, print it out and take some photos of it.
2020

@@ -26,20 +26,20 @@ finder.computeObjectImagePoints(vecImg);
2626
vector<Mat> objectPoints = finder.getObjectPoints();
2727
vector<Mat> imagePoints = finder.getImagePoints();
2828
```
29-
Here variable ```patternWidth``` and ```patternHeight``` are physical pattern width and height with some user defined unit. ```vecImg``` is a vector of images that stores calibration images.
29+
Here the variables ```patternWidth``` and ```patternHeight``` refer to the physical dimensions of the calibration object in the chosen unit of measurement. ```vecImg``` is a vector of images that stores calibration images.
3030

3131
Second, use calibration functions like ```cv::calibrateCamera``` or ```cv::omnidir::calibrate``` to calibrate camera.
3232

3333
Multiple Cameras Calibration
3434
-------------------------------
3535
Now we move to multiple camera calibration, so far this toolbox must use random pattern object.
3636

37-
To calibrate multiple cameras, we first need to take some photos of random pattern. Of cause, to calibrate the extrinsic parameters, one pattern need to be viewed by multiple cameras (at least two) at the same time. Another thing is that to help the program know which camera and which pattern the photo is taken, the image file should be named as "cameraIdx-timestamp.*". Photos with same timestamp means that they are the same object taken by several cameras. In addition, cameraIdx should start from 0. Some examples of files names are "0-129.png", "0-187.png", "1-187", "2-129".
37+
To calibrate multiple cameras, we first need to take some photos of random pattern. Of course, to calibrate the extrinsic parameters, one pattern needs to be viewed by multiple cameras (at least two) at the same time. Another thing is that to help the program know which camera and which pattern the photo is taken, the image file should be named as "cameraIdx-timestamp.*". Photos with same timestamp means that they are the same object taken by several cameras. In addition, cameraIdx should start from 0. Some examples of files names are "0-129.png", "0-187.png", "1-187", "2-129".
3838

3939
Then, we can run multiple cameras calibration as
4040
```
4141
cv::multicalib::MultiCameraCalibration multiCalib(cameraType, nCamera, inputFilename,patternWidth, patternHeight, showFeatureExtraction, nMiniMatches);
4242
multiCalib.run();
4343
multiCalib.writeParameters(outputFilename);
4444
```
45-
Here ```cameraType``` indicates the camera type, ```multicalib::MultiCameraCalibration::PINHOLE``` and ```multicalib::MultiCameraCalibration::OMNIDIRECTIONAL``` are supported. For omnidirectional camera, you can refer to ```cv::omnidir``` module for detail. ```nCamera``` is the number of camers. ```inputFilename``` is the name of a file generated by ```imagelist_creator``` from ```opencv/sample```. It stores names of random pattern and calibration images, the first file name is the name of random pattern. ```patternWidth``` and ```patternHeight``` are physical width and height of pattern. ```showFeatureExtraction``` is a flags to indicate whether show feature extraction process. ```nMiniMatches``` is a minimal points that should be detected in each frame, otherwise this frame will be abandoned. ```outputFilename``` is a xml file name to store parameters.
45+
Here ```cameraType``` indicates the camera type, ```multicalib::MultiCameraCalibration::PINHOLE``` and ```multicalib::MultiCameraCalibration::OMNIDIRECTIONAL``` are supported. For omnidirectional camera, you can refer to ```cv::omnidir``` module for detail. ```nCamera``` is the number of cameras. ```inputFilename``` is the name of a file generated by ```imagelist_creator``` from ```opencv/sample```. It stores names of random pattern and calibration images, the first file name is the name of random pattern. ```patternWidth``` and ```patternHeight``` represents the physical width and height of the pattern. ```showFeatureExtraction``` is a boolean flag that determines whether the feature extraction process is displayed. ```nMiniMatches``` is the minimum number of points that should be detected in each frame, otherwise this frame will be abandoned. ```outputFilename``` is an XML that will store the calibration parameters.

modules/cudaarithm/include/opencv2/cudaarithm.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -546,12 +546,16 @@ static inline void scaleAdd(InputArray src1, double alpha, InputArray src2, Outp
546546

547547
/** @brief Applies a fixed-level threshold to each array element.
548548
549+
The special value cv::THRESH_OTSU may be combined with one of the other types. In this case, the function determines the
550+
optimal threshold value using the Otsu's and uses it instead of the specified threshold. The function returns the
551+
computed threshold value in addititon to the thresholded matrix.
552+
The Otsu's method is implemented only for 8-bit matrices.
553+
549554
@param src Source array (single-channel).
550-
@param dst Destination array with the same size and type as src .
555+
@param dst Destination array with the same size and type as src.
551556
@param thresh Threshold value.
552557
@param maxval Maximum value to use with THRESH_BINARY and THRESH_BINARY_INV threshold types.
553-
@param type Threshold type. For details, see threshold . The THRESH_OTSU and THRESH_TRIANGLE
554-
threshold types are not supported.
558+
@param type Threshold type. For details, see threshold. The THRESH_TRIANGLE threshold type is not supported.
555559
@param stream Stream for the asynchronous version.
556560
557561
@sa threshold

modules/cudaarithm/src/cuda/threshold.cu

Lines changed: 245 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,256 @@ namespace
9595
}
9696
}
9797

98-
double cv::cuda::threshold(InputArray _src, OutputArray _dst, double thresh, double maxVal, int type, Stream& stream)
98+
99+
__global__ void otsu_sums(uint *histogram, uint *threshold_sums, unsigned long long *sums)
100+
{
101+
const uint n_bins = 256;
102+
103+
__shared__ uint shared_memory_ts[n_bins];
104+
__shared__ unsigned long long shared_memory_s[n_bins];
105+
106+
int bin_idx = threadIdx.x;
107+
int threshold = blockIdx.x;
108+
109+
uint threshold_sum_above = 0;
110+
unsigned long long sum_above = 0;
111+
112+
if (bin_idx > threshold)
113+
{
114+
uint value = histogram[bin_idx];
115+
threshold_sum_above = value;
116+
sum_above = value * bin_idx;
117+
}
118+
119+
blockReduce<n_bins>(shared_memory_ts, threshold_sum_above, bin_idx, plus<uint>());
120+
blockReduce<n_bins>(shared_memory_s, sum_above, bin_idx, plus<unsigned long long>());
121+
122+
if (bin_idx == 0)
123+
{
124+
threshold_sums[threshold] = threshold_sum_above;
125+
sums[threshold] = sum_above;
126+
}
127+
}
128+
129+
__global__ void
130+
otsu_variance(float2 *variance, uint *histogram, uint *threshold_sums, unsigned long long *sums)
131+
{
132+
const uint n_bins = 256;
133+
134+
__shared__ signed long long shared_memory_a[n_bins];
135+
__shared__ signed long long shared_memory_b[n_bins];
136+
137+
int bin_idx = threadIdx.x;
138+
int threshold = blockIdx.x;
139+
140+
uint n_samples = threshold_sums[0];
141+
uint n_samples_above = threshold_sums[threshold];
142+
uint n_samples_below = n_samples - n_samples_above;
143+
144+
unsigned long long total_sum = sums[0];
145+
unsigned long long sum_above = sums[threshold];
146+
unsigned long long sum_below = total_sum - sum_above;
147+
148+
float threshold_variance_above_f32 = 0;
149+
float threshold_variance_below_f32 = 0;
150+
if (bin_idx > threshold)
151+
{
152+
float mean = (float) sum_above / n_samples_above;
153+
float sigma = bin_idx - mean;
154+
threshold_variance_above_f32 = sigma * sigma;
155+
}
156+
else
157+
{
158+
float mean = (float) sum_below / n_samples_below;
159+
float sigma = bin_idx - mean;
160+
threshold_variance_below_f32 = sigma * sigma;
161+
}
162+
163+
uint bin_count = histogram[bin_idx];
164+
signed long long threshold_variance_above_i64 = (signed long long)(threshold_variance_above_f32 * bin_count);
165+
signed long long threshold_variance_below_i64 = (signed long long)(threshold_variance_below_f32 * bin_count);
166+
blockReduce<n_bins>(shared_memory_a, threshold_variance_above_i64, bin_idx, plus<signed long long>());
167+
blockReduce<n_bins>(shared_memory_b, threshold_variance_below_i64, bin_idx, plus<signed long long>());
168+
169+
if (bin_idx == 0)
170+
{
171+
variance[threshold] = make_float2(threshold_variance_above_i64, threshold_variance_below_i64);
172+
}
173+
}
174+
175+
template <uint n_thresholds>
176+
__device__ bool has_lowest_score(
177+
uint threshold, float original_score, float score, uint *shared_memory
178+
) {
179+
// It may happen that multiple threads have the same minimum score. In that case, we want to find the thread with
180+
// the lowest threshold. This is done by calling '__syncthreads_count' to count how many threads have a score
181+
// that matches to the minimum score found. Since this is rare, we will optimize towards the common case where only
182+
// one thread has the minimum score. If multiple threads have the same minimum score, we will find the minimum
183+
// threshold that satifies the condition
184+
bool has_match = original_score == score;
185+
uint matches = __syncthreads_count(has_match);
186+
187+
if(matches > 1) {
188+
// If this thread has a match, we use it; otherwise we give it a value that is larger than the maximum
189+
// threshold, so it will never get picked
190+
uint min_threshold = has_match ? threshold : n_thresholds;
191+
192+
blockReduce<n_thresholds>(shared_memory, min_threshold, threshold, minimum<uint>());
193+
194+
return min_threshold == threshold;
195+
} else {
196+
return has_match;
197+
}
198+
}
199+
200+
__global__ void
201+
otsu_score(uint *otsu_threshold, uint *threshold_sums, float2 *variance)
202+
{
203+
const uint n_thresholds = 256;
204+
205+
__shared__ float shared_memory[n_thresholds];
206+
207+
int threshold = threadIdx.x;
208+
209+
uint n_samples = threshold_sums[0];
210+
uint n_samples_above = threshold_sums[threshold];
211+
uint n_samples_below = n_samples - n_samples_above;
212+
213+
float threshold_mean_above = (float)n_samples_above / n_samples;
214+
float threshold_mean_below = (float)n_samples_below / n_samples;
215+
216+
float2 variances = variance[threshold];
217+
float variance_above = n_samples_above > 0 ? variances.x / n_samples_above : 0.0f;
218+
float variance_below = n_samples_below > 0 ? variances.y / n_samples_below : 0.0f;
219+
220+
float above = threshold_mean_above * variance_above;
221+
float below = threshold_mean_below * variance_below;
222+
float score = above + below;
223+
224+
float original_score = score;
225+
226+
blockReduce<n_thresholds>(shared_memory, score, threshold, minimum<float>());
227+
228+
if (threshold == 0)
229+
{
230+
shared_memory[0] = score;
231+
}
232+
__syncthreads();
233+
234+
score = shared_memory[0];
235+
236+
// We found the minimum score, but in some cases multiple threads can have the same score, so we need to find the
237+
// lowest threshold
238+
if (has_lowest_score<n_thresholds>(threshold, original_score, score, (uint *) shared_memory))
239+
{
240+
*otsu_threshold = threshold;
241+
}
242+
}
243+
244+
void compute_otsu(uint *histogram, uint *otsu_threshold, Stream &stream)
245+
{
246+
const uint n_bins = 256;
247+
const uint n_thresholds = 256;
248+
249+
cudaStream_t cuda_stream = StreamAccessor::getStream(stream);
250+
251+
dim3 block_all(n_bins);
252+
dim3 grid_all(n_thresholds);
253+
dim3 block_score(n_thresholds);
254+
dim3 grid_score(1);
255+
256+
BufferPool pool(stream);
257+
GpuMat gpu_threshold_sums(1, n_bins, CV_32SC1, pool.getAllocator());
258+
GpuMat gpu_sums(1, n_bins, CV_64FC1, pool.getAllocator());
259+
GpuMat gpu_variances(1, n_bins, CV_32FC2, pool.getAllocator());
260+
261+
otsu_sums<<<grid_all, block_all, 0, cuda_stream>>>(
262+
histogram, gpu_threshold_sums.ptr<uint>(), gpu_sums.ptr<unsigned long long>());
263+
otsu_variance<<<grid_all, block_all, 0, cuda_stream>>>(
264+
gpu_variances.ptr<float2>(), histogram, gpu_threshold_sums.ptr<uint>(), gpu_sums.ptr<unsigned long long>());
265+
otsu_score<<<grid_score, block_score, 0, cuda_stream>>>(
266+
otsu_threshold, gpu_threshold_sums.ptr<uint>(), gpu_variances.ptr<float2>());
267+
}
268+
269+
// TODO: Replace this is cv::cuda::calcHist
270+
template <uint n_bins>
271+
__global__ void histogram_kernel(
272+
uint *histogram, const uint8_t *image, uint width,
273+
uint height, uint pitch)
274+
{
275+
__shared__ uint local_histogram[n_bins];
276+
277+
uint x = blockIdx.x * blockDim.x + threadIdx.x;
278+
uint y = blockIdx.y * blockDim.y + threadIdx.y;
279+
uint tid = threadIdx.y * blockDim.x + threadIdx.x;
280+
281+
if (tid < n_bins)
282+
{
283+
local_histogram[tid] = 0;
284+
}
285+
286+
__syncthreads();
287+
288+
if (x < width && y < height)
289+
{
290+
uint8_t value = image[y * pitch + x];
291+
atomicInc(&local_histogram[value], 0xFFFFFFFF);
292+
}
293+
294+
__syncthreads();
295+
296+
if (tid < n_bins)
297+
{
298+
cv::cudev::atomicAdd(&histogram[tid], local_histogram[tid]);
299+
}
300+
}
301+
302+
// TODO: Replace this with cv::cuda::calcHist
303+
void calcHist(
304+
const GpuMat src, GpuMat histogram, Stream stream)
305+
{
306+
const uint n_bins = 256;
307+
308+
cudaStream_t cuda_stream = StreamAccessor::getStream(stream);
309+
310+
dim3 block(128, 4, 1);
311+
dim3 grid = dim3(divUp(src.cols, block.x), divUp(src.rows, block.y), 1);
312+
CV_CUDEV_SAFE_CALL(cudaMemsetAsync(histogram.ptr<uint>(), 0, n_bins * sizeof(uint), cuda_stream));
313+
histogram_kernel<n_bins>
314+
<<<grid, block, 0, cuda_stream>>>(
315+
histogram.ptr<uint>(), src.ptr<uint8_t>(), (uint) src.cols, (uint) src.rows, (uint) src.step);
316+
}
317+
318+
double cv::cuda::threshold(InputArray _src, OutputArray _dst, double thresh, double maxVal, int type, Stream &stream)
99319
{
100320
GpuMat src = getInputMat(_src, stream);
101321

102322
const int depth = src.depth();
103323

324+
const int THRESH_OTSU = 8;
325+
if ((type & THRESH_OTSU) == THRESH_OTSU)
326+
{
327+
CV_Assert(depth == CV_8U);
328+
CV_Assert(src.channels() == 1);
329+
330+
BufferPool pool(stream);
331+
332+
// Find the threshold using Otsu and then run the normal thresholding algorithm
333+
GpuMat gpu_histogram(256, 1, CV_32SC1, pool.getAllocator());
334+
calcHist(src, gpu_histogram, stream);
335+
336+
GpuMat gpu_otsu_threshold(1, 1, CV_32SC1, pool.getAllocator());
337+
compute_otsu(gpu_histogram.ptr<uint>(), gpu_otsu_threshold.ptr<uint>(), stream);
338+
339+
cv::Mat mat_otsu_threshold;
340+
gpu_otsu_threshold.download(mat_otsu_threshold, stream);
341+
stream.waitForCompletion();
342+
343+
// Overwrite the threshold value with the Otsu value and remove the Otsu flag from the type
344+
type = type & ~THRESH_OTSU;
345+
thresh = (double) mat_otsu_threshold.at<int>(0);
346+
}
347+
104348
CV_Assert( depth <= CV_64F );
105349
CV_Assert( type <= 4 /*THRESH_TOZERO_INV*/ );
106350

0 commit comments

Comments
 (0)