001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image.contour;
031
032import java.io.PrintWriter;
033import java.io.StringWriter;
034import java.util.ArrayList;
035import java.util.Iterator;
036import java.util.List;
037
038import org.openimaj.image.pixel.Pixel;
039import org.openimaj.math.geometry.shape.Polygon;
040import org.openimaj.math.geometry.shape.Rectangle;
041
042/**
043 * A contour or hierarchical set of contours within an image.
044 * 
045 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
046 */
047public class Contour extends Polygon {
048        /**
049         * The type of contour
050         */
051        public ContourType type;
052        /**
053         * sub contours
054         */
055        public List<Contour> children = new ArrayList<Contour>();
056        /**
057         * The parent contour, might be null
058         */
059        public Contour parent;
060
061        /**
062         * where the contour starts
063         */
064        public Pixel start;
065        Rectangle rect = null;
066
067        /**
068         * Construct a contour with the given type and start at the origin
069         * 
070         * @param type
071         *            the type
072         */
073        public Contour(ContourType type) {
074                this.type = type;
075                this.start = new Pixel(0, 0);
076        }
077
078        /**
079         * Construct a contour with the given type and start pixel
080         * 
081         * @param type
082         *            the type
083         * @param x
084         *            the x-ordinate of the start pixel
085         * @param y
086         *            the y-ordinate of the start pixel
087         */
088        public Contour(ContourType type, int x, int y) {
089                this.type = type;
090                this.start = new Pixel(x, y);
091        }
092
093        /**
094         * Construct a contour with the given type and start pixel
095         * 
096         * @param type
097         *            the type
098         * @param p
099         *            the coordinate of the start pixel
100         */
101        public Contour(ContourType type, Pixel p) {
102                this.type = type;
103                this.start = p;
104        }
105
106        /**
107         * Construct a contour with the given starting coordinate and a
108         * <code>null</code> type.
109         * 
110         * @param x
111         *            the x-ordinate of the start pixel
112         * @param y
113         *            the y-ordinate of the start pixel
114         */
115        public Contour(int x, int y) {
116                this.type = null;
117                this.start = new Pixel(x, y);
118        }
119
120        protected void setParent(Contour bp) {
121                this.parent = bp;
122                bp.children.add(this);
123        }
124
125        @Override
126        public String toString() {
127                final StringWriter contour = new StringWriter();
128                final PrintWriter pw = new PrintWriter(contour);
129                pw.println(String.format("[%s] start: %s %s", this.type, this.start, this.points));
130                for (final Contour child : this.children) {
131                        pw.print(child);
132                }
133                pw.flush();
134                return contour.toString();
135        }
136
137        /**
138         * Complete this contour by computing it's bounding box
139         */
140        public void finish() {
141                this.rect = this.calculateRegularBoundingBox();
142        }
143
144        private class ContourIterator implements Iterator<Contour> {
145                final List<Contour> toProcess = new ArrayList<Contour>();
146
147                ContourIterator() {
148                        toProcess.add(Contour.this);
149                }
150
151                @Override
152                public boolean hasNext() {
153                        return !toProcess.isEmpty();
154                }
155
156                @Override
157                public Contour next() {
158                        final Contour next = toProcess.remove(toProcess.size() - 1);
159                        toProcess.addAll(next.children);
160                        return next;
161                }
162
163                @Override
164                public void remove() {
165                        throw new UnsupportedOperationException("Removal not supported");
166                }
167        }
168
169        /**
170         * Get an iterator over the complete set of contours (including the root
171         * itself)
172         * 
173         * @return the iterator
174         */
175        public Iterator<Contour> contourIterator() {
176                return new ContourIterator();
177        }
178
179        /**
180         * Get an {@link Iterable} over all the contours belonging to this root
181         * (including the root itself)
182         * 
183         * @return the iterable
184         */
185        public Iterable<Contour> contourIterable() {
186                return new Iterable<Contour>() {
187                        @Override
188                        public Iterator<Contour> iterator() {
189                                return contourIterator();
190                        }
191                };
192        }
193
194        @Override
195        public boolean isHole() {
196                return type != null && type.equals(ContourType.HOLE);
197        }
198}