/* * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package boofcv.alg.segmentation.ms; import boofcv.alg.segmentation.ComputeRegionMeanColor; import boofcv.struct.ConnectRule; import boofcv.struct.image.GrayF32; import boofcv.struct.image.GrayS32; import boofcv.struct.image.GrayU8; import org.ddogleg.struct.FastQueue; import org.ddogleg.struct.GrowQueue_I32; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * @author Peter Abeles */ public class TestMergeSmallRegions { /** * Runs everything to remove the small patches. This test hsa been designed to take multiple * passes to complete. */ @Test public void process() { GrayU8 image = new GrayU8(10,9); image.data = new byte[]{ 0,0,0,5,5,5,0,0,0,0, 0,0,0,5,5,5,7,8,0,0, 0,0,0,5,6,5,0,0,0,0, 0,0,0,5,5,5,0,0,0,0, 0,0,0,5,5,5,0,0,0,7, 0,0,0,5,5,4,4,0,0,8, 0,0,0,0,0,0,0,0,9,9, 0,0,0,0,0,0,9,9,9,9, 0,0,0,0,0,0,9,9,6,7}; GrayS32 pixelToRegion = new GrayS32(10,9); pixelToRegion.data = new int[]{ 0,0,0,1,1,1,0,0,0,0, 0,0,0,1,1,1,4,5,0,0, 0,0,0,1,2,1,0,0,0,0, 0,0,0,1,1,1,0,0,0,0, 0,0,0,1,1,1,0,0,0,9, 0,0,0,1,1,3,3,0,0,9, 0,0,0,0,0,0,0,0,6,6, 0,0,0,0,0,0,6,6,6,6, 0,0,0,0,0,0,6,6,7,8}; GrowQueue_I32 memberCount = new GrowQueue_I32(); memberCount.resize(10); for( int i = 0; i < pixelToRegion.data.length; i++ ) { memberCount.data[pixelToRegion.data[i]]++; } FastQueue<float[]> regionColor = new FastQueue<float[]>(float[].class,true) { protected float[] createInstance() {return new float[ 1 ];} }; regionColor.resize(10); ComputeRegionMeanColor<GrayU8> mean = new ComputeRegionMeanColor.U8(); mean.process(image,pixelToRegion,memberCount,regionColor); MergeSmallRegions<GrayU8> alg = new MergeSmallRegions<>(3, ConnectRule.FOUR,mean); alg.process(image, pixelToRegion, memberCount, regionColor); // check the results. Should only be three regions assertEquals(3,memberCount.size); assertEquals(3,regionColor.size); GrowQueue_I32 memberExpected = new GrowQueue_I32(3); memberExpected.resize(3); for( int i = 0; i < pixelToRegion.data.length; i++ ) { memberExpected.data[pixelToRegion.data[i]]++; } for( int i = 0; i < 3; i++ ) assertEquals(memberExpected.get(i),memberCount.get(i)); // simple sanity check assertTrue(memberExpected.get(0)>memberExpected.get(1)); } @Test public void setupPruneList() { GrowQueue_I32 regionMemberCount = new GrowQueue_I32(); regionMemberCount.size = 6; regionMemberCount.data = new int[]{10,11,20,20,10,20}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(11,ConnectRule.FOUR,null); assertTrue(alg.setupPruneList(regionMemberCount)); assertEquals(2,alg.pruneGraph.size()); assertEquals(0,alg.pruneGraph.get(0).segment); assertEquals(4,alg.pruneGraph.get(1).segment); assertEquals(0,alg.pruneGraph.get(0).edges.size); assertEquals(0,alg.pruneGraph.get(1).edges.size); boolean flags[] = new boolean[]{true,false,false,false,true,false}; for( int i = 0; i <6; i++ ) assertEquals(flags[i],alg.segmentPruneFlag.get(i)); assertEquals(0,alg.segmentToPruneID.get(0)); assertEquals(1,alg.segmentToPruneID.get(4)); } @Test public void findAdjacentRegions_center() { int N = 9; GrayS32 pixelToRegion = new GrayS32(5,4); pixelToRegion.data = new int[] {1,1,1,1,1, 1,2,3,4,1, 1,5,6,7,1, 1,8,8,8,1}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(10,ConnectRule.FOUR,null); alg.initializeMerge(N); alg.segmentPruneFlag.resize(N); alg.pruneGraph.reset(); alg.segmentToPruneID.resize(N); alg.segmentPruneFlag.set(2,true); alg.segmentPruneFlag.set(5,true); alg.segmentToPruneID.set(2,0); alg.segmentToPruneID.set(5,1); alg.pruneGraph.grow().init(2); alg.pruneGraph.grow().init(5); alg.findAdjacentRegions(pixelToRegion); // See expected if the graph is constructed int edges2[] = new int[]{1,3,5}; int edges5[] = new int[]{1,2,6,8}; checkNode(alg, edges2,0); checkNode(alg, edges5,1); } @Test public void findAdjacentRegions_right() { int N = 9; GrayS32 pixelToRegion = new GrayS32(5,4); pixelToRegion.data = new int[] {1,1,1,1,2, 1,1,1,1,3, 1,1,1,7,4, 1,1,1,1,5}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(10,ConnectRule.FOUR,null); alg.initializeMerge(N); alg.segmentPruneFlag.resize(N); alg.pruneGraph.reset(); alg.segmentToPruneID.resize(N); alg.segmentPruneFlag.set(2,true); alg.segmentPruneFlag.set(4,true); alg.segmentPruneFlag.set(5,true); alg.segmentToPruneID.set(2,0); alg.segmentToPruneID.set(4,1); alg.segmentToPruneID.set(5,2); alg.pruneGraph.grow().init(2); alg.pruneGraph.grow().init(4); alg.pruneGraph.grow().init(5); alg.findAdjacentRegions(pixelToRegion); // See expected if the graph is constructed int edges2[] = new int[]{1,3}; int edges4[] = new int[]{3,5,7}; int edges5[] = new int[]{1,4}; checkNode(alg, edges2,0); checkNode(alg, edges4,1); checkNode(alg, edges5,2); } @Test public void findAdjacentRegions_bottom() { int N = 9; GrayS32 pixelToRegion = new GrayS32(5,4); pixelToRegion.data = new int[] {1,1,1,1,1, 1,1,1,1,1, 4,1,1,1,1, 2,1,1,3,5}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(10,ConnectRule.FOUR,null); alg.initializeMerge(N); alg.segmentPruneFlag.resize(N); alg.pruneGraph.reset(); alg.segmentToPruneID.resize(N); alg.segmentPruneFlag.set(2,true); alg.segmentPruneFlag.set(3,true); alg.segmentPruneFlag.set(5,true); alg.segmentToPruneID.set(2,0); alg.segmentToPruneID.set(3,1); alg.segmentToPruneID.set(5,2); alg.pruneGraph.grow().init(2); alg.pruneGraph.grow().init(3); alg.pruneGraph.grow().init(5); alg.findAdjacentRegions(pixelToRegion); // See expected if the graph is constructed int edges2[] = new int[]{1,4}; int edges3[] = new int[]{1,5}; int edges5[] = new int[]{1,3}; checkNode(alg, edges2,0); checkNode(alg, edges3,1); checkNode(alg, edges5,2); } @Test public void selectMerge() { int N = 10; MergeSmallRegions alg = new MergeSmallRegions(10,ConnectRule.FOUR,null); alg.initializeMerge(N); FastQueue<float[]> regionColor = new FastQueue<>(float[].class, false); for( int i = 0; i < N; i++ ) { regionColor.add( new float[3]); } // make it so the closest color to 2 is 4 regionColor.data[2] = new float[]{1,2,3}; regionColor.data[4] = new float[]{1.1f,2,3}; regionColor.data[6] = new float[]{100,2,3}; MergeSmallRegions.Node n = (MergeSmallRegions.Node)alg.pruneGraph.grow(); // mark 4 and 6 as being connect to 2 n.init(2); n.edges.add(4); n.edges.add(6); alg.selectMerge(0,regionColor); // doesn't matter which one is merged into which if( alg.mergeList.get(4) == 4 ) { assertEquals(4, alg.mergeList.get(2)); assertEquals(4, alg.mergeList.get(4)); } else { assertEquals(2, alg.mergeList.get(4)); assertEquals(2, alg.mergeList.get(2)); } } private void checkNode(MergeSmallRegions<GrayF32> alg, int[] edges, int pruneId) { assertEquals(edges.length,alg.pruneGraph.get(pruneId).edges.size); for( int i = 0; i < edges.length; i++ ) { assertTrue(alg.pruneGraph.get(pruneId).isConnected(edges[i])); } } /** * Make sure a connect-4 rule is correctly enforced */ @Test public void adjacentInner4() { int N = 9; GrayS32 pixelToRegion = new GrayS32(5,4); pixelToRegion.data = new int[] {1,2,3,0,0, 8,9,4,0,0, 7,6,5,0,0, 0,0,0,0,0}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(10,ConnectRule.FOUR,null); alg.initializeMerge(N); alg.segmentPruneFlag.resize(N); alg.pruneGraph.reset(); alg.segmentToPruneID.resize(N+1); alg.segmentPruneFlag.set(1,true); alg.segmentPruneFlag.set(9,true); alg.segmentToPruneID.set(1,0); alg.segmentToPruneID.set(9,1); alg.pruneGraph.grow().init(1); alg.pruneGraph.grow().init(9); alg.adjacentInner4(pixelToRegion); // See expected if the graph is constructed int edges1[] = new int[]{2,8}; int edges9[] = new int[]{2,4,6,8}; checkNode(alg, edges1,0); checkNode(alg, edges9,1); } @Test public void adjacentInner8() { int N = 13; GrayS32 pixelToRegion = new GrayS32(5,4); pixelToRegion.data = new int[] {1,2,3,10,0, 8,9,4,11,0, 7,6,5,12,0, 0,0,0,0,0}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(10,ConnectRule.EIGHT,null); alg.initializeMerge(N); alg.segmentPruneFlag.resize(N); alg.pruneGraph.reset(); alg.segmentToPruneID.resize(N); alg.segmentPruneFlag.set(1,true); alg.segmentPruneFlag.set(2,true); alg.segmentPruneFlag.set(4,true); alg.segmentToPruneID.set(1,0); alg.segmentToPruneID.set(2,1); alg.segmentToPruneID.set(4,2); alg.pruneGraph.grow().init(1); alg.pruneGraph.grow().init(2); alg.pruneGraph.grow().init(4); alg.adjacentInner8(pixelToRegion); // See expected if the graph is constructed int edges1[] = new int[]{}; int edges2[] = new int[]{3,4,9,8}; int edges4[] = new int[]{2,3,10,11,12,5,6,9}; checkNode(alg, edges1,0); checkNode(alg, edges2,1); checkNode(alg, edges4,2); } @Test public void adjacentBorder8() { int N = 13; GrayS32 pixelToRegion = new GrayS32(5,4); pixelToRegion.data = new int[] {0,0,0, 0,0, 8,2,0, 9,4, 7,1,3, 5,11, 0,0,6,10,12}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(10,ConnectRule.EIGHT,null); alg.initializeMerge(N); alg.segmentPruneFlag.resize(N); alg.pruneGraph.reset(); alg.segmentToPruneID.resize(N); alg.segmentPruneFlag.set(8,true); alg.segmentPruneFlag.set(4,true); alg.segmentPruneFlag.set(12,true); alg.segmentToPruneID.set(8,0); alg.segmentToPruneID.set(4,1); alg.segmentToPruneID.set(12,2); alg.pruneGraph.grow().init(8); alg.pruneGraph.grow().init(4); alg.pruneGraph.grow().init(12); alg.adjacentBorder(pixelToRegion); // See expected if the graph is constructed int edges8[] = new int[]{0,2,1,7}; int edges4[] = new int[]{0,9,11,5}; int edges12[] = new int[]{5,10,11}; checkNode(alg, edges8,0); checkNode(alg, edges4,1); checkNode(alg, edges12,2); } @Test public void adjacentBorder4() { int N = 13; GrayS32 pixelToRegion = new GrayS32(5,4); pixelToRegion.data = new int[] {0,0,0, 0,0, 8,2,0, 9,4, 7,1,3, 5,11, 0,0,6,10,12}; MergeSmallRegions<GrayF32> alg = new MergeSmallRegions(10,ConnectRule.FOUR,null); alg.initializeMerge(N); alg.segmentPruneFlag.resize(N); alg.pruneGraph.reset(); alg.segmentToPruneID.resize(N); alg.segmentPruneFlag.set(8,true); alg.segmentPruneFlag.set(4,true); alg.segmentPruneFlag.set(12,true); alg.segmentToPruneID.set(8,0); alg.segmentToPruneID.set(4,1); alg.segmentToPruneID.set(12,2); alg.pruneGraph.grow().init(8); alg.pruneGraph.grow().init(4); alg.pruneGraph.grow().init(12); alg.adjacentBorder(pixelToRegion); // See expected if the graph is constructed int edges8[] = new int[]{}; int edges4[] = new int[]{0,9,11}; int edges12[] = new int[]{10,11}; checkNode(alg, edges8,0); checkNode(alg, edges4,1); checkNode(alg, edges12,2); } }