|
36 | 36 | #include "precomp.hpp" |
37 | 37 | #include <math.h> |
38 | 38 | #include <vector> |
| 39 | +#include <stack> |
39 | 40 | #include <iostream> |
40 | 41 |
|
41 | 42 |
|
@@ -807,6 +808,150 @@ CV_EXPORTS void morphologyEx(InputArray rlSrc, OutputArray rlDest, int op, Input |
807 | 808 | } |
808 | 809 | } |
809 | 810 |
|
| 811 | +static void getNeighborLookup(std::vector<int>& lutNeighbor, int connectivity, const rlVec& runs, const std::vector<int>& pIdxChord1, const std::vector<int>& pIdxNextRow, int emptyValue) |
| 812 | +{ |
| 813 | + CV_Assert(connectivity == 8 || connectivity == 4); |
| 814 | + CV_Assert(lutNeighbor.size() != 0 && lutNeighbor.size() == 4u * runs.size()); |
| 815 | + CV_Assert(runs.back().r - runs[0].r + 1 == (int)pIdxChord1.size() && pIdxChord1.size() == pIdxNextRow.size()); |
| 816 | + const int connectionOffset = (connectivity == 4) ? 1 : 0; |
| 817 | + int nRows = (int)pIdxChord1.size(); |
| 818 | + for (int i = 0; i < nRows - 1; ++i) |
| 819 | + { |
| 820 | + int firstRowStart = pIdxChord1[i]; |
| 821 | + int secondRowStart = pIdxChord1[i + 1]; |
| 822 | + int firstRowEnd = pIdxNextRow[i] - 1; |
| 823 | + int secondRowEnd = pIdxNextRow[i + 1] - 1; |
| 824 | + if (firstRowStart != emptyValue && secondRowStart != emptyValue) |
| 825 | + { |
| 826 | + while (firstRowStart <= firstRowEnd && secondRowStart <= secondRowEnd) |
| 827 | + { |
| 828 | + const rlType& first = runs[firstRowStart]; |
| 829 | + const rlType& second = runs[secondRowStart]; |
| 830 | + // Compare runs between two consecutive rows sequentially |
| 831 | + if ((first.ce + connectionOffset < second.cb)) |
| 832 | + { |
| 833 | + firstRowStart++; |
| 834 | + } |
| 835 | + else if ((first.cb > second.ce + connectionOffset)) |
| 836 | + { |
| 837 | + secondRowStart++; |
| 838 | + } |
| 839 | + else |
| 840 | + { |
| 841 | + // Each run stores indices of its 4-connected neighbors in lutNeighbor: |
| 842 | + // [0] = Top-leftmost neighbor |
| 843 | + // [1] = Top-rightmost neighbor |
| 844 | + // [2] = Bottom-leftmost neighbor |
| 845 | + // [3] = Bottom-rightmost neighbor |
| 846 | + lutNeighbor[firstRowStart * 4 + 3] = secondRowStart; |
| 847 | + lutNeighbor[secondRowStart * 4 + 1] = firstRowStart; |
| 848 | + if (lutNeighbor[firstRowStart * 4 + 2] == emptyValue) |
| 849 | + { |
| 850 | + lutNeighbor[firstRowStart * 4 + 2] = secondRowStart; |
| 851 | + } |
| 852 | + if (lutNeighbor[secondRowStart * 4] == emptyValue) |
| 853 | + { |
| 854 | + lutNeighbor[secondRowStart * 4] = firstRowStart; |
| 855 | + } |
| 856 | + if ((first.ce == second.ce)) |
| 857 | + { |
| 858 | + firstRowStart++; |
| 859 | + secondRowStart++; |
| 860 | + } |
| 861 | + else if ((first.ce > second.ce)) |
| 862 | + { |
| 863 | + secondRowStart++; |
| 864 | + } |
| 865 | + else |
| 866 | + { |
| 867 | + firstRowStart++; |
| 868 | + } |
| 869 | + } |
| 870 | + } |
| 871 | + } |
| 872 | + } |
| 873 | +} |
| 874 | + |
| 875 | +// @author Yu Changming, [email protected], https://github.com/yuchangminghit |
| 876 | +CV_EXPORTS void fillHoles(InputArray rlSrc, OutputArray rlDest, int connectivity) |
| 877 | +{ |
| 878 | + CV_Assert(connectivity == 8 || connectivity == 4); |
| 879 | + rlVec runsSource, runsDestination; |
| 880 | + Size sizeSource; |
| 881 | + convertInputArrayToRuns(rlSrc, runsSource, sizeSource); |
| 882 | + if ((int)runsSource.size() <= 3) |
| 883 | + { |
| 884 | + convertToOutputArray(runsSource, sizeSource, rlDest); |
| 885 | + return; |
| 886 | + } |
| 887 | + rlVec borderRect, backgroundRuns; |
| 888 | + cv::Rect boundingRect = getBoundingRectangle(runsSource); |
| 889 | + if (boundingRect.width < 3 || boundingRect.height < 3) |
| 890 | + { |
| 891 | + convertToOutputArray(runsSource, sizeSource, rlDest); |
| 892 | + return; |
| 893 | + } |
| 894 | + createUprightRectangle(cv::Rect(boundingRect.x - 1, boundingRect.y - 1, boundingRect.width + 2, boundingRect.height + 2), borderRect); |
| 895 | + subtract_rle(borderRect, runsSource, backgroundRuns); |
| 896 | + int nMinRow = backgroundRuns[0].r; |
| 897 | + int nMaxRow = backgroundRuns.back().r; |
| 898 | + int nRows = nMaxRow - nMinRow + 1; |
| 899 | + const int EMPTY = -1; |
| 900 | + std::vector<int> pIdxChord1(nRows, EMPTY); |
| 901 | + std::vector<int> pIdxNextRow(nRows, EMPTY); |
| 902 | + pIdxChord1[0] = 0; |
| 903 | + pIdxNextRow[nRows - 1] = (int)backgroundRuns.size(); |
| 904 | + for (int i = 1; i < (int)backgroundRuns.size(); i++) { |
| 905 | + if (backgroundRuns[i].r != backgroundRuns[i - 1].r) { |
| 906 | + pIdxChord1[backgroundRuns[i].r - nMinRow] = i; |
| 907 | + pIdxNextRow[backgroundRuns[i - 1].r - nMinRow] = i; |
| 908 | + } |
| 909 | + } |
| 910 | + std::vector<int> neighborLUT(4u * backgroundRuns.size(), EMPTY); |
| 911 | + getNeighborLookup(neighborLUT, connectivity, backgroundRuns, pIdxChord1, pIdxNextRow, EMPTY); |
| 912 | + // Stack stores run indices to be processed |
| 913 | + std::stack<int> indexStack; |
| 914 | + indexStack.push(0); |
| 915 | + // Flags for each run: |
| 916 | + // 0: Run is adjacent to the outer background (not part of a hole) |
| 917 | + // 255: Run is isolated from the outer background (part of a hole region) |
| 918 | + std::vector<uchar> selectedFlags(backgroundRuns.size(), EMPTY); |
| 919 | + const int SELECTED = 0; |
| 920 | + selectedFlags[0] = SELECTED; |
| 921 | + const int neighborOffsets[] = { 0, 2 }; |
| 922 | + while (!indexStack.empty()) |
| 923 | + { |
| 924 | + int currentIdx = indexStack.top(); |
| 925 | + indexStack.pop(); |
| 926 | + for (int i = 0; i < 2; i++) |
| 927 | + { |
| 928 | + if (neighborLUT[4 * currentIdx + neighborOffsets[i]] != EMPTY) |
| 929 | + { |
| 930 | + int startIdx = neighborLUT[4 * currentIdx + neighborOffsets[i]]; |
| 931 | + int endIdx = neighborLUT[4 * currentIdx + neighborOffsets[i] + 1]; |
| 932 | + for (int nbrIdx = startIdx; nbrIdx <= endIdx; ++nbrIdx) |
| 933 | + { |
| 934 | + if (selectedFlags[nbrIdx] != SELECTED) |
| 935 | + { |
| 936 | + selectedFlags[nbrIdx] = SELECTED; |
| 937 | + indexStack.push(nbrIdx); |
| 938 | + } |
| 939 | + } |
| 940 | + } |
| 941 | + } |
| 942 | + } |
| 943 | + rlVec holesBackground; |
| 944 | + for (int i = 0; i < (int)selectedFlags.size(); i++) |
| 945 | + { |
| 946 | + if (selectedFlags[i] == SELECTED) |
| 947 | + { |
| 948 | + holesBackground.push_back(backgroundRuns[i]); |
| 949 | + } |
| 950 | + } |
| 951 | + invertRegion(holesBackground, runsDestination); |
| 952 | + convertToOutputArray(runsDestination, sizeSource, rlDest); |
| 953 | +} |
| 954 | + |
810 | 955 | } |
811 | 956 | } //end of cv::ximgproc |
812 | 957 | } //end of cv |
0 commit comments