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.demos.sandbox;
031
032import gnu.trove.list.TIntList;
033import gnu.trove.list.array.TIntArrayList;
034
035import java.io.IOException;
036import java.net.MalformedURLException;
037
038import org.openimaj.image.FImage;
039import org.openimaj.image.MBFImage;
040import org.openimaj.image.colour.RGBColour;
041import org.openimaj.image.processing.threshold.OtsuThreshold;
042import org.openimaj.video.VideoDisplay;
043import org.openimaj.video.VideoDisplayListener;
044import org.openimaj.video.capture.VideoCapture;
045
046public class QRTester {
047        public static void main(String[] args) throws MalformedURLException, IOException {
048                // MBFImage cimg = ImageUtilities.readMBF(new
049                // URL("http://cdn.socialnomics.net/wp-content/uploads/2011/03/QR-code-girl1.jpg"));
050                // // MBFImage cimg = ImageUtilities.readMBF(new
051                // URL("http://thinkd2c.files.wordpress.com/2011/05/qrcode_wwd.png"));
052                // findMarkers(cimg);
053                // DisplayUtilities.display(cimg);
054
055                final VideoDisplay<MBFImage> display = VideoDisplay.createVideoDisplay(new VideoCapture(640, 480));
056                display.addVideoListener(new VideoDisplayListener<MBFImage>() {
057
058                        @Override
059                        public void beforeUpdate(MBFImage frame) {
060                                findMarkers(frame);
061                        }
062
063                        @Override
064                        public void afterUpdate(VideoDisplay<MBFImage> display) {
065                                // TODO Auto-generated method stub
066
067                        }
068                });
069        }
070
071        static void findMarkers(MBFImage cimg) {
072                FImage image = cimg.flatten();
073                image = image.processInplace(new OtsuThreshold());
074                // image = image.threshold(0.2f);
075
076                for (int y = 0; y < image.height; y += 2) {
077                        final TIntArrayList centres = processLineH(image, y);
078
079                        for (final int x : centres.toArray()) {
080                                cimg.drawLine(x, y - 10, x, y + 10, RGBColour.RED);
081                                cimg.drawLine(x - 10, y, x + 10, y, RGBColour.RED);
082                        }
083                }
084                // cimg.internalAssign(new MBFImage(image,image,image));
085        }
086
087        static TIntArrayList processLineH(FImage image, int y) {
088                final TIntArrayList counts = new TIntArrayList();
089
090                int start = 0;
091                while (start < image.width) {
092                        if (image.pixels[y][start] == 0)
093                                break;
094                        start++;
095                }
096
097                for (int i = start; i < image.width; i++) {
098                        int count = 0;
099                        final float state = image.pixels[y][i];
100                        for (; i < image.width; i++) {
101                                if (image.pixels[y][i] != state) {
102                                        i--; // step back because the outer loop increments
103                                        break;
104                                }
105                                count++;
106                        }
107                        counts.add(count);
108                }
109
110                return findPossibleH(counts, start);
111        }
112
113        static TIntArrayList findPossibleH(TIntArrayList counts, final int start) {
114                final TIntArrayList centers = new TIntArrayList();
115
116                // assume first count is black. Only need check patterns starting with
117                // black...
118                for (int i = 0, co = start; i < counts.size() - 5; i += 2) {
119                        final TIntList pattern = counts.subList(i, i + 5);
120
121                        if (isValid(pattern)) {
122                                int sum = 0;
123                                for (final int j : pattern.toArray())
124                                        sum += j;
125
126                                centers.add(co + (sum / 2));
127                        }
128                        co += counts.get(i) + counts.get(i + 1);
129                }
130                return centers;
131        }
132
133        private static boolean isValid(TIntList pattern) {
134                // 1 1 3 1 1
135                // B W B W B
136
137                final float[] apat = { 1, 1, 3, 1, 1 };
138                final float[] fpat = { pattern.get(0), pattern.get(1), pattern.get(2), pattern.get(3), pattern.get(4) };
139
140                // System.out.print(Arrays.toString(fpat) + "\t\t");
141
142                final float ratio = 4 / (fpat[0] + fpat[1] + fpat[3] + fpat[4]);
143                for (int i = 0; i < 5; i++)
144                        fpat[i] *= ratio;
145
146                float error = 0;
147                for (int i = 0; i < 5; i++) {
148                        final float diff = apat[i] - fpat[i];
149                        error += diff * diff;
150                }
151
152                // System.out.println(error);
153                // System.out.println(Arrays.toString(fpat) + "\t\t" + error);
154                if (error < 0.5)
155                        return true;
156
157                return false;
158        }
159
160}