\texttt{dst} (x,y) = \fork{0}{if $\texttt{src}(x,y) > \texttt{thresh}$}{\texttt{src}(x,y)}{otherwise}
-Also, the special value ``THRESH_OTSU`` may be combined with
-one of the above values. In this case, the function determines the optimal threshold
-value using the Otsu's algorithm and uses it instead of the specified ``thresh`` .
+Also, the special values ``THRESH_OTSU`` or ``THRESH_TRIANGLE`` may be combined with
+one of the above values. In these cases, the function determines the optimal threshold
+value using the Otsu's or Triangle algorithm and uses it instead of the specified ``thresh`` .
The function returns the computed threshold value.
-Currently, the Otsu's method is implemented only for 8-bit images.
+Currently, the Otsu's and Triangle methods are implemented only for 8-bit images.
.. image:: pics/threshold.png
THRESH_TOZERO = 3, // value = value > threshold ? value : 0
THRESH_TOZERO_INV = 4, // value = value > threshold ? 0 : value
THRESH_MASK = 7,
- THRESH_OTSU = 8 // use Otsu algorithm to choose the optimal threshold value
+ THRESH_OTSU = 8, // use Otsu algorithm to choose the optimal threshold value
+ THRESH_TRIANGLE = 16 // use Triangle algorithm to choose the optimal threshold value
};
//! adaptive threshold algorithm
CV_THRESH_TOZERO =3, /* value = value > threshold ? value : 0 */
CV_THRESH_TOZERO_INV =4, /* value = value > threshold ? 0 : value */
CV_THRESH_MASK =7,
- CV_THRESH_OTSU =8 /* use Otsu algorithm to choose the optimal threshold value;
+ CV_THRESH_OTSU =8, /* use Otsu algorithm to choose the optimal threshold value;
combine the flag with one of the above CV_THRESH_* values */
+ CV_THRESH_TRIANGLE =16 /* use Triangle algorithm to choose the optimal threshold value;
+ combine the flag with one of the above CV_THRESH_* values, but not
+ with CV_THRESH_OTSU */
};
/* Adaptive threshold methods */
return max_val;
}
+static double
+getThreshVal_Triangle_8u( const Mat& _src )
+{
+ Size size = _src.size();
+ int step = (int) _src.step;
+ if( _src.isContinuous() )
+ {
+ size.width *= size.height;
+ size.height = 1;
+ step = size.width;
+ }
+
+ const int N = 256;
+ int i, j, h[N] = {0};
+ for( i = 0; i < size.height; i++ )
+ {
+ const uchar* src = _src.ptr() + step*i;
+ j = 0;
+ #if CV_ENABLE_UNROLLED
+ for( ; j <= size.width - 4; j += 4 )
+ {
+ int v0 = src[j], v1 = src[j+1];
+ h[v0]++; h[v1]++;
+ v0 = src[j+2]; v1 = src[j+3];
+ h[v0]++; h[v1]++;
+ }
+ #endif
+ for( ; j < size.width; j++ )
+ h[src[j]]++;
+ }
+
+ int left_bound = 0, right_bound = 0, max_ind = 0, max = 0;
+ int temp;
+ bool isflipped = false;
+
+ for( i = 0; i < N; i++ )
+ {
+ if( h[i] > 0 )
+ {
+ left_bound = i;
+ break;
+ }
+ }
+ if( left_bound > 0 )
+ left_bound--;
+
+ for( i = N-1; i > 0; i-- )
+ {
+ if( h[i] > 0 )
+ {
+ right_bound = i;
+ break;
+ }
+ }
+ if( right_bound < N-1 )
+ right_bound++;
+
+ for( i = 0; i < N; i++ )
+ {
+ if( h[i] > max)
+ {
+ max = h[i];
+ max_ind = i;
+ }
+ }
+
+ if( max_ind-left_bound < right_bound-max_ind)
+ {
+ isflipped = true;
+ i = 0, j = N-1;
+ while( i < j )
+ {
+ temp = h[i]; h[i] = h[j]; h[j] = temp;
+ i++; j--;
+ }
+ left_bound = N-1-right_bound;
+ max_ind = N-1-max_ind;
+ }
+
+ double thresh = left_bound;
+ double a, b, dist = 0, tempdist;
+
+ /*
+ * We do not need to compute precise distance here. Distance is maximized, so some constants can
+ * be omitted. This speeds up a computation a bit.
+ */
+ a = max; b = left_bound-max_ind;
+ for( i = left_bound+1; i <= max_ind; i++ )
+ {
+ tempdist = a*i + b*h[i];
+ if( tempdist > dist)
+ {
+ dist = tempdist;
+ thresh = i;
+ }
+ }
+ thresh--;
+
+ if( isflipped )
+ thresh = N-1-thresh;
+
+ return thresh;
+}
+
class ThresholdRunner : public ParallelLoopBody
{
public:
Mat src = _src.getMat();
bool use_otsu = (type & THRESH_OTSU) != 0;
+ bool use_triangle = (type & THRESH_TRIANGLE) != 0;
type &= THRESH_MASK;
if( use_otsu )
thresh = getThreshVal_Otsu_8u(src);
}
+ if( use_triangle )
+ {
+ CV_Assert( src.type() == CV_8UC1 );
+ thresh = getThreshVal_Triangle_8u(src);
+ }
+
_dst.create( src.size(), src.type() );
Mat dst = _dst.getMat();