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.feature;
031
032import java.util.AbstractList;
033import java.util.AbstractMap;
034import java.util.AbstractSet;
035import java.util.Iterator;
036import java.util.Set;
037
038import org.openimaj.data.dataset.GroupedDataset;
039import org.openimaj.data.dataset.ListBackedDataset;
040import org.openimaj.data.dataset.ListDataset;
041import org.openimaj.data.dataset.MapBackedDataset;
042
043/**
044 * Static methods to generate datasets of features from datasets of objects
045 * using a feature extractor.
046 * 
047 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
048 */
049public class DatasetExtractors {
050        private DatasetExtractors() {
051        }
052
053        /**
054         * Create a {@link ListDataset} of features from the given
055         * {@link ListDataset} of objects by extracting the features from the
056         * objects with the given feature extractor.
057         * <p>
058         * This function is produces a lazy {@link ListDataset} instance that
059         * extracts features on demand. No caching is performed, so if you
060         * <code>get</code> from the resultant list for the same element many times,
061         * you'll invoke the feature extractor each time.
062         * 
063         * @param input
064         *            the {@link ListDataset} of input objects
065         * @param extractor
066         *            the feature extractor
067         * @return a {@link ListDataset} of features
068         */
069        public static <FEATURE, OBJECT> ListDataset<FEATURE> createLazyFeatureDataset(final ListDataset<OBJECT> input,
070                        final FeatureExtractor<FEATURE, OBJECT> extractor)
071        {
072                return new ListBackedDataset<FEATURE>(new AbstractList<FEATURE>() {
073                        @Override
074                        public FEATURE get(int index) {
075                                return extractor.extractFeature(input.get(index));
076                        }
077
078                        @Override
079                        public int size() {
080                                return input.size();
081                        }
082                });
083        }
084
085        /**
086         * Create a {@link GroupedDataset} of keys to {@link ListDataset} of
087         * features from the given {@link GroupedDataset} of keys to
088         * {@link ListDataset}s of objects by extracting the features from the
089         * objects with the given feature extractor.
090         * <p>
091         * This function is produces a lazy {@link GroupedDataset} instance that
092         * extracts features on demand. No caching is performed, so if you
093         * <code>get</code> from the resultant list for the same element many times,
094         * you'll invoke the feature extractor each time.
095         * 
096         * @param input
097         *            the {@link GroupedDataset} of input objects
098         * @param extractor
099         *            the feature extractor
100         * @return a {@link GroupedDataset} of features
101         */
102        public static <FEATURE, OBJECT, KEY> GroupedDataset<KEY, ListDataset<FEATURE>, FEATURE> createLazyFeatureDataset(
103                        final GroupedDataset<KEY, ? extends ListDataset<OBJECT>, OBJECT> input,
104                        final FeatureExtractor<FEATURE, OBJECT> extractor)
105        {
106                return new MapBackedDataset<KEY, ListDataset<FEATURE>, FEATURE>(new AbstractMap<KEY, ListDataset<FEATURE>>() {
107                        @Override
108                        public int size() {
109                                return input.size();
110                        }
111
112                        @Override
113                        public boolean isEmpty() {
114                                return input.isEmpty();
115                        }
116
117                        @Override
118                        public boolean containsKey(Object key) {
119                                return input.containsKey(key);
120                        }
121
122                        @Override
123                        public ListDataset<FEATURE> get(Object key) {
124                                return createLazyFeatureDataset(input.get(key), extractor);
125                        }
126
127                        @Override
128                        public Set<KEY> keySet() {
129                                return input.keySet();
130                        }
131
132                        @Override
133                        public Set<java.util.Map.Entry<KEY, ListDataset<FEATURE>>> entrySet() {
134                                return new AbstractSet<Entry<KEY, ListDataset<FEATURE>>>() {
135
136                                        @Override
137                                        public Iterator<java.util.Map.Entry<KEY, ListDataset<FEATURE>>> iterator() {
138                                                return new Iterator<Entry<KEY, ListDataset<FEATURE>>>() {
139                                                        Iterator<?> internal = input.entrySet().iterator();
140
141                                                        @Override
142                                                        public boolean hasNext() {
143                                                                return internal.hasNext();
144                                                        }
145
146                                                        @Override
147                                                        public Entry<KEY, ListDataset<FEATURE>> next() {
148                                                                return new Entry<KEY, ListDataset<FEATURE>>() {
149                                                                        @SuppressWarnings("unchecked")
150                                                                        Entry<KEY, OBJECT> next = (Entry<KEY, OBJECT>) internal.next();
151
152                                                                        @Override
153                                                                        public KEY getKey() {
154                                                                                return next.getKey();
155                                                                        }
156
157                                                                        @SuppressWarnings("unchecked")
158                                                                        @Override
159                                                                        public ListDataset<FEATURE> getValue() {
160                                                                                return createLazyFeatureDataset((ListDataset<OBJECT>) next.getValue(), extractor);
161                                                                        }
162
163                                                                        @Override
164                                                                        public ListDataset<FEATURE> setValue(ListDataset<FEATURE> value) {
165                                                                                throw new UnsupportedOperationException();
166                                                                        }
167                                                                };
168                                                        }
169
170                                                        @Override
171                                                        public void remove() {
172                                                                throw new UnsupportedOperationException();
173                                                        }
174                                                };
175                                        }
176
177                                        @Override
178                                        public int size() {
179                                                return input.size();
180                                        }
181                                };
182                        }
183
184                });
185        }
186}