001package org.openimaj.processing;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import org.openimaj.image.MBFImage;
007import org.openimaj.image.processing.face.detection.DetectedFace;
008import org.openimaj.image.processing.face.detection.HaarCascadeDetector;
009import org.openimaj.image.processing.resize.ResizeProcessor;
010import org.openimaj.math.geometry.shape.Rectangle;
011import org.openimaj.video.capture.VideoCapture;
012import org.openimaj.video.capture.VideoCaptureException;
013
014import processing.core.PApplet;
015import processing.core.PConstants;
016import processing.core.PImage;
017import processing.core.PShape;
018
019/**
020 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
021 *
022 */
023public class OpenIMAJ implements PConstants{
024
025        private static final int DEFAULT_WIDTH = 640;
026        private static final int DEFAULT_HEIGHT = 480;
027        PApplet parent;
028        private MBFImage oiImage;
029        private HaarCascadeDetector faceDetector;
030        private VideoCapture capture;
031
032        /**
033         * @param parent
034         */
035        public OpenIMAJ(PApplet parent) {
036                this();
037                this.parent = parent;
038
039                parent.registerMethod("dispose", this);
040                parent.registerMethod("pre", this);
041        }
042
043        /**
044         *
045         */
046        public OpenIMAJ() {
047                faceDetector = new HaarCascadeDetector(80);
048        }
049
050        /**
051         * Initialise face detection with minimum face size
052         * @param min
053         */
054        public void initFace(int min){
055                faceDetector = new HaarCascadeDetector(min);
056        }
057
058        /**
059         * Start a video capture, default size, default device
060         */
061        public void startCapture(){
062                try {
063                        this.capture = new VideoCapture(DEFAULT_WIDTH, DEFAULT_HEIGHT);
064                } catch (VideoCaptureException e) {
065                }
066        }
067
068        /**
069         * Initialise video capture on the default device
070         *
071         * @param width
072         * @param height
073         */
074        public void startCapture(int width, int height){
075                try {
076                        this.capture = new VideoCapture(width, height);
077                } catch (VideoCaptureException e) {
078                }
079        }
080        /**
081         * Initialise video capture
082         *
083         * @param width
084         * @param height
085         * @param device
086         */
087        public void startCapture(int width, int height, int device){
088                try {
089                        this.capture = new VideoCapture(width, height,VideoCapture.getVideoDevices().get(device));
090                } catch (VideoCaptureException e) {
091                }
092        }
093
094        /**
095         * Given an initialised video capture, capture a {@link PImage}
096         * @return capture
097         */
098        public PImage capturePImage(){
099                MBFImage frame = this.capture.getNextFrame();
100                return asPImage(frame);
101        }
102        /**
103         * Given an initialised video capture, capture a {@link PImage}
104         * @param setToCurrentFrame whether the current openimaj frame (for analysis) should be set from capture
105         * @return capture
106         */
107        public PImage capturePImage(boolean setToCurrentFrame){
108
109                MBFImage frame = this.capture.getNextFrame();
110                if(setToCurrentFrame){
111                        this.oiImage = frame.clone();
112                }
113                return asPImage(frame);
114        }
115
116        /**
117         * Capture an {@link MBFImage}
118         * @return
119         */
120        public MBFImage capture(){
121                MBFImage frame = this.capture.getNextFrame();
122                return frame;
123        }
124        public MBFImage capture(boolean setToCurrentFrame){
125
126                MBFImage frame = this.capture.getNextFrame();
127                if(setToCurrentFrame){
128                        this.oiImage = frame.clone();
129                }
130                return frame;
131        }
132
133        public PImage asPImage(MBFImage frame) {
134                PImage img = this.parent.createImage(frame.getWidth(), frame.getHeight(), RGB);
135                img.pixels = frame.toPackedARGBPixels();
136                return img;
137        }
138
139        /**
140         *
141         */
142        public void pre(){
143        }
144        /**
145         * Updates the OpenIMAJ held {@link MBFImage} instance from the whole parent {@link PApplet}
146         */
147        public void updateImage() {
148                this.parent.loadPixels();
149                updateImage(this.parent.pixels,this.parent.width,this.parent.height);
150        }
151
152        /**
153         * @param capture
154         */
155        public void updateImage(PImage capture){
156                updateImage(capture.pixels,capture.width, capture.height);
157        }
158
159        /**
160         * @param capture
161         */
162        public void updateImage(MBFImage capture){
163                this.oiImage = capture;
164        }
165
166        /**
167         * Updates the OpenIMAJ held {@link MBFImage} instance
168         * @param pixels the pixels to use as the MBFImage
169         * @param width the width of the image
170         * @param height the height of the image
171         */
172        public void updateImage(int[] pixels,int width, int height) {
173                this.oiImage = new MBFImage(pixels,width, height);
174        }
175        /**
176         *
177         */
178        public void dispose() {
179                this.oiImage = null;
180        }
181
182        public void resize(int width, int height){
183                if(this.oiImage == null) return;
184                this.oiImage.processInplace(new ResizeProcessor(width, height));
185        }
186
187        /**
188         * Detect faces using {@link HaarCascadeDetector}, return an {@link ArrayList} of
189         * {@link PShape} instances. Note the {@link PShape} instances have no fill and
190         * a colour: 255,0,0
191         * @return detected faces
192         */
193        public ArrayList<PShape> faces(){
194                ArrayList<PShape> faces = new ArrayList<PShape>();
195                List<DetectedFace> detected = faceDetector.detectFaces(oiImage.flatten());
196                for (DetectedFace detectedFace : detected) {
197                        Rectangle bounds = detectedFace.getBounds();
198                        PShape detectedPShape = this.parent.createShape(RECT,bounds.x,bounds.y,bounds.width,bounds.height);
199
200                        detectedPShape.setFill(false);
201                        detectedPShape.setStroke(this.parent.color(255f, 0, 0));
202                        faces.add(detectedPShape);
203                }
204                return faces;
205        }
206}