/******************************************************* * Copyright (c) 2019, ArrayFire * All rights reserved. * * This file is distributed under 3-clause BSD license. * The complete license agreement can be obtained at: * http://arrayfire.com/licenses/BSD-3-Clause ********************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include using af::dim4; using common::createSpanIndex; using detail::arithOp; using detail::Array; using detail::cast; using detail::createValueArray; using detail::reduce_all; using detail::uchar; using detail::uint; using detail::ushort; using std::conditional; using std::is_same; using std::sqrt; using std::swap; /// Index corner points of given seed points template Array pointList(const Array& in, const Array& x, const Array& y) { af_array xcoords = getHandle(x); af_array ycoords = getHandle(y); std::array idxrs = {{{{xcoords}, false, false}, {{ycoords}, false, false}, createSpanIndex(), createSpanIndex()}}; Array retVal = detail::index(in, idxrs.data()); // detail::index fn keeps a reference to detail::Array // created from the xcoords/ycoords passed via idxrs. // Hence, it is safe to release xcoords, ycoords releaseHandle(xcoords); releaseHandle(ycoords); return retVal; } /// Returns the sum of all values given the four corner points of the region of /// interest in the integral-image/summed-area-table of an input image. /// /// +-------------------------------------+ /// | | | | /// | A(_x, _y)| B(_x, y_)| | /// |-----------+----------------+ | /// | |@@@@@@@@@@@@@@@@| | /// | |@@@@@@@@@@@@@@@@| | /// | |@@@@@@@@@@@@@@@@| | /// | |@@@@@@@@@@@@@@@@| | /// |-----------+----------------+ | /// | C(x_, _y) D(x_, y_) | /// | | /// +-------------------------------------+ template Array sum(const Array& sat, const Array& _x, const Array& x_, const Array& _y, const Array& y_) { Array A = pointList(sat, _x, _y); Array B = pointList(sat, _x, y_); Array C = pointList(sat, x_, _y); Array D = pointList(sat, x_, y_); Array DA = arithOp(D, A, D.dims()); Array BC = arithOp(B, C, B.dims()); return arithOp(DA, BC, DA.dims()); } template af_array ccHelper(const Array& img, const Array& seedx, const Array& seedy, const unsigned radius, const unsigned mult, const unsigned iterations, const double segmentedValue) { using CT = typename conditional::value, double, float>::type; constexpr CT epsilon = 1.0e-6; auto calcVar = [](CT s2, CT s1, CT n) -> CT { CT retVal = CT(0); if (n > 1) { retVal = (s2 - (s1 * s1 / n)) / (n - CT(1)); } return retVal; }; const dim4& inDims = img.dims(); const dim4& seedDims = seedx.dims(); const size_t numSeeds = seedx.elements(); const unsigned nhoodLen = 2 * radius + 1; const unsigned nhoodSize = nhoodLen * nhoodLen; auto labelSegmented = [segmentedValue, inDims](const Array& segmented) { Array newVals = createValueArray(inDims, CT(segmentedValue)); Array result = arithOp(newVals, segmented, inDims); // cast final result to input type return cast(result); }; Array radiip = createValueArray(seedDims, radius + 1); Array radii = createValueArray(seedDims, radius); Array _x = arithOp(seedx, radiip, seedDims); Array x_ = arithOp(seedx, radii, seedDims); Array _y = arithOp(seedy, radiip, seedDims); Array y_ = arithOp(seedy, radii, seedDims); Array in = common::convRange(img, CT(1), CT(2)); Array in_2 = arithOp(in, in, inDims); Array I1 = common::integralImage(in); Array I2 = common::integralImage(in_2); Array S1 = sum(I1, _x, x_, _y, y_); Array S2 = sum(I2, _x, x_, _y, y_); CT totSum = reduce_all(S1); CT totSumSq = reduce_all(S2); CT totalNum = numSeeds * nhoodSize; CT s1mean = totSum / totalNum; CT s1var = calcVar(totSumSq, totSum, totalNum); CT s1stddev = sqrt(s1var); CT lower = s1mean - mult * s1stddev; CT upper = s1mean + mult * s1stddev; Array seedIntensities = pointList(in, seedx, seedy); CT maxSeedIntensity = reduce_all(seedIntensities); CT minSeedIntensity = reduce_all(seedIntensities); if (lower > minSeedIntensity) { lower = minSeedIntensity; } if (upper < maxSeedIntensity) { upper = maxSeedIntensity; } Array segmented = floodFill(in, seedx, seedy, CT(1), lower, upper); if (std::abs(s1var) < epsilon) { // If variance is close to zero, stop after initial segmentation return getHandle(labelSegmented(segmented)); } bool continueLoop = true; for (uint i = 0; (i < iterations) && continueLoop; ++i) { // Segmented images are set with 1's and 0's thus essentially // making them into mask arrays for each iteration's input image uint sampleCount = reduce_all(segmented, true); if (sampleCount == 0) { // If no valid pixels are found, skip iterations break; } Array valids = arithOp(segmented, in, inDims); Array vsqrd = arithOp(valids, valids, inDims); CT validsSum = reduce_all(valids, true); CT sumOfSqs = reduce_all(vsqrd, true); CT validsMean = validsSum / sampleCount; CT validsVar = calcVar(sumOfSqs, validsSum, CT(sampleCount)); CT stddev = sqrt(validsVar); CT newLow = validsMean - mult * stddev; CT newHigh = validsMean + mult * stddev; if (newLow > minSeedIntensity) { newLow = minSeedIntensity; } if (newHigh < maxSeedIntensity) { newHigh = maxSeedIntensity; } if (std::abs(validsVar) < epsilon) { // If variance is close to zero, discontinue iterating. continueLoop = false; } segmented = floodFill(in, seedx, seedy, CT(1), newLow, newHigh); } return getHandle(labelSegmented(segmented)); } af_err af_confidence_cc(af_array* out, const af_array in, const af_array seedx, const af_array seedy, const unsigned radius, const unsigned multiplier, const int iter, const double segmented_value) { try { const ArrayInfo& inInfo = getInfo(in); const ArrayInfo& seedxInfo = getInfo(seedx); const ArrayInfo& seedyInfo = getInfo(seedy); const af::dim4& inputDimensions = inInfo.dims(); const af::dtype inputArrayType = inInfo.getType(); // TODO(pradeep) handle case where seeds are towards border // and indexing may result in throwing exception // TODO(pradeep) add batch support later ARG_ASSERT( 1, (inputDimensions.ndims() > 0 && inputDimensions.ndims() <= 2)); ARG_ASSERT(2, (seedxInfo.ndims() == 1)); ARG_ASSERT(3, (seedyInfo.ndims() == 1)); ARG_ASSERT(2, (seedxInfo.elements() == seedyInfo.elements())); af_array output = 0; switch (inputArrayType) { case f32: output = ccHelper(getArray(in), getArray(seedx), getArray(seedy), radius, multiplier, iter, segmented_value); break; case u32: output = ccHelper(getArray(in), getArray(seedx), getArray(seedy), radius, multiplier, iter, segmented_value); break; case u16: output = ccHelper(getArray(in), getArray(seedx), getArray(seedy), radius, multiplier, iter, segmented_value); break; case u8: output = ccHelper(getArray(in), getArray(seedx), getArray(seedy), radius, multiplier, iter, segmented_value); break; default: TYPE_ERROR(0, inputArrayType); } swap(*out, output); } CATCHALL; return AF_SUCCESS; }