Package barcode

Source Code of barcode.ReadBarcode

package barcode;
/*
* ReadBarcode.java
*
* Created on September 13, 2006, 11:19 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*
* Copyright 2006 by Jon A. Webb
*     This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU Lesser General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU Lesser General Public License for more details.
*
*    You should have received a copy of the Lesser GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/
import java.util.Vector;

import jjil.algorithm.Gray8CannyHoriz;
import jjil.algorithm.Gray8Rgb;
import jjil.algorithm.Gray8Crop;
import jjil.algorithm.Gray8HistEq;
import jjil.algorithm.Gray8RectStretch;
import jjil.algorithm.Gray8Reduce;
import jjil.algorithm.Gray8TrapWarp;
import jjil.algorithm.Gray8VertAvg;
import jjil.algorithm.LinefitHough;
import jjil.algorithm.RgbSelectGray;
import jjil.algorithm.Gray8ZeroCrossingHoriz;
import jjil.core.Gray8Image;
import jjil.core.Image;
import jjil.core.Point;
import jjil.core.Rect;
import jjil.core.RgbImage;
import jjil.core.Sequence;
import jjil.debug.Debug;

/** Read an image barcode using a variety of different barcode formats, and
* return the best match.
*
* @author webb
*/
public class ReadBarcode {
    private int dBestLeftPos;
    private int dBestRightPos;
    private Gray8Image imageCropped;
    private Gray8Image imageEdge;
    private Gray8Image imageRectified;
    private Rect rectBarcode;
    private String szBestCode;
    private String szBestName;
    private int wBestMatch;
    private int wSlopeLeft;
    private int wSlopeRight;
    private int cYLeft;
    private int cYRight;
   
    /** Creates a new instance of ReadBarcode */
    public ReadBarcode() {
    }
   
    /**
     * Crop the image to a specified cropping window and detect edges
     * using an appropriate Canny operator. The cWidth of the Canny operator
     * is set based on the cWidth of the cropped image, so that wider images
     * use lower-frequency edge detection.
     * <p>
     * This method assigns a value to the imageCropped and imageEdge fields.
     *
     * @param image the input image.
     * @param dTopLeftX top left corner of the cropping window, x coordinate
     * @param dTopLeftX top left corner of the cropping window, y coordinate
     * @param cWidth cWidth of the cropping window
     * @param cHeight height of the cropping window
     */
    private void cropAndDetectEdges(
            Image image,
            int dTopLeftX,
            int dTopLeftY,
            int cWidth,
            int cHeight) throws jjil.core.Error
    {
        /* Build an image pipeline to do the work.
         */
        Sequence seq = new Sequence();
        /* First convert the color image to black and white by selecting the
         * green channel. We use the green channel because it is the highest
         * resolution channel on the CCDs used in cellphones.
         */
        if (image instanceof RgbImage) {
            seq.add(new RgbSelectGray(RgbSelectGray.GREEN));
        }
        /* Now crop the gray image.
         */
        int dLeft = Math.max(0, dTopLeftX - cWidth / 12);
        int cWidthExp = Math.min(image.getWidth() - dLeft, cWidth * 7 / 6);
        seq.add(new Gray8Crop(dLeft, dTopLeftY, cWidthExp, cHeight));
        /* Apply the pipeline to get the cropped image.
         */
        seq.push(image);
        Image imageResult = seq.getFront();
        if (!(imageResult instanceof Gray8Image)) {
            throw new jjil.core.Error(
            jjil.core.Error.PACKAGE.ALGORITHM,
            jjil.algorithm.ErrorCodes.IMAGE_NOT_GRAY8IMAGE,
            imageResult.toString(),
            null,
            null);
        }
        this.imageCropped = (Gray8Image) imageResult;
        {   Debug debug = new Debug();
          Gray8Rgb g2r = new Gray8Rgb();
          g2r.push(this.imageCropped);
          RgbImage rgb = (RgbImage) g2r.getFront();
          debug.toFile(rgb, "cropped.png"); //$NON-NLS-1$
        }
        /* Then do edge detection. The cWidth of the Canny operator is set
         * based on the cropped cWidth, which is supposed to be the
         * barcode cWidth.
         */
        int cCannyWidth = cWidth / 6;
        Gray8CannyHoriz canny = new Gray8CannyHoriz(cCannyWidth);
        /* Now apply the edge detection to the cropped mage
         */
        canny.push(imageCropped.clone());
        /* And obtain the result
         */
        imageResult = canny.getFront();
        if (!(imageResult instanceof Gray8Image)) {
            throw new jjil.core.Error(
            jjil.core.Error.PACKAGE.ALGORITHM,
            jjil.algorithm.ErrorCodes.IMAGE_NOT_GRAY8IMAGE,
            imageResult.toString(),
            null,
            null);
        }
        this.imageEdge = (Gray8Image) imageResult;
        {  
          Debug debug = new Debug();
        Gray8Rgb g2r = new Gray8Rgb();
        g2r.push(this.imageEdge);
        RgbImage rgb = (RgbImage) g2r.getFront();
        debug.toFile(rgb, "edges.png"); //$NON-NLS-1$
      }
    }
   
    private static Image cropAndStretchImage(
            Image imageInput,
            int dLeft,
            int dRight,
            int cHeightReduce,
            int cInputWidth,
            int cTargetWidth) throws jjil.core.Error
    {
        // so it is divisible by cHeightReduce, a power of 2
        int cCroppedHeight = cHeightReduce *
                (imageInput.getHeight() / cHeightReduce);
        int cTotalWidth = dRight - dLeft;
        Gray8Crop crop = new Gray8Crop(dLeft, 0, cTotalWidth, cCroppedHeight);
        Sequence seq = new Sequence(crop);
        seq.add(new Gray8Reduce(1,cHeightReduce));
        int cReducedHeight = cCroppedHeight / cHeightReduce;
        // we stretch the cropped image so cInputWidth becomes cTargetWidth. This means
        // cTotalWidth must become cTargetWidth * cTotalWidth / cInputWidth
        Gray8RectStretch stretch = new Gray8RectStretch(
                cTargetWidth * cTotalWidth / cInputWidth, cReducedHeight);
        seq.add(stretch);
        seq.add(new Gray8HistEq());
        seq.push(imageInput);
        return seq.getFront();
    }
   
    private boolean findBarcodeEdges() throws jjil.core.Error
    {
        Gray8ZeroCrossingHoriz zeroesFind = new Gray8ZeroCrossingHoriz(8);
        int[][] wZeroes = zeroesFind.push(this.imageEdge);
        Vector v = getPointsFromZeroes(wZeroes, true);
        if (v.size() < 5) {
            return false;
        }
        LinefitHough fit =
                new LinefitHough(0, this.imageEdge.getWidth(), -76, 76, 100);
        fit.push(v);
        if (fit.getCount() < 5) {
            return false;
        }
        this.cYLeft = fit.getY();
        this.wSlopeLeft = fit.getSlope();
        v = getPointsFromZeroes(wZeroes, false);
        if (v.size() < 5) {
            return false;
        }
        fit.push(v);
        if (fit.getCount() < 5) {
            return false;
        }
        this.cYRight = fit.getY();
        this.wSlopeRight = fit.getSlope();
        {
          Debug debug = new Debug();
          Gray8Rgb g2r = new Gray8Rgb();
          g2r.push(this.imageEdge);
          debug.toFile((RgbImage) g2r.getFront(), "edges.png"); //$NON-NLS-1$
        }
        return true;
     }
   
    private void findPreciseBarcodeLimits()
    {
        int cWidth = this.imageRectified.getWidth();
        int cShortWidth = cWidth / 24,
            cLongWidth = cWidth / 4;
        byte[] bAvg = Gray8VertAvg.push(this.imageRectified);
        int[] wShortSum = new int[bAvg.length],
                wLongSum = new int[bAvg.length];
        formShortAndLongSums(bAvg, cShortWidth, cLongWidth, wShortSum, wLongSum);
        this.dBestLeftPos = -1;
        this.dBestRightPos = -1;
        int wBestLeftJump = Integer.MIN_VALUE,
                wBestRightJump = Integer.MIN_VALUE;
        for (int i=0; i<cWidth; i++) {
            if (i < cWidth / 3 && i-cShortWidth >= 0 && i+cLongWidth < cWidth) {
                int wLeftJump = wShortSum[i-cShortWidth] * cLongWidth -
                        wLongSum[i+cLongWidth] * cShortWidth;
                if (wLeftJump > wBestLeftJump) {
                        this.dBestLeftPos = i;
                        wBestLeftJump = wLeftJump;
                }
            }
            if (i > cWidth * 2 / 3 && i-cLongWidth >= 0 && i+cShortWidth < cWidth) {
                int wRightJump = wShortSum[i+cShortWidth] * cLongWidth -
                        wLongSum[i-cLongWidth] * cShortWidth;
                if (wRightJump > wBestRightJump) {
                        this.dBestRightPos = i;
                        wBestRightJump = wRightJump;
                }
            }
        }
    }

    private static void formShortAndLongSums(
            byte[] bAvg,
            int cShortWidth,
            int cLongWidth,
            int[] wShortSumResult,
            int[] wLongSumResult)
    {
        int wShortSum = 0, wLongSum = 0;
        for (int i=0; i<cShortWidth; i++) {
            wShortSum += bAvg[i];
        }
        for (int i=0; i<cLongWidth; i++) {
            wLongSum += bAvg[i];
        }
        for (int i=0; i<bAvg.length; i++) {
            wShortSumResult[i] = wShortSum;
            wLongSumResult[i] = wLongSum;
            if (i+cShortWidth < bAvg.length) {
                wShortSum += bAvg[i+cShortWidth];
            }
            if (i+cLongWidth < bAvg.length) {
                wLongSum += bAvg[i+cLongWidth];
            }
            if (i-cShortWidth >= 0) {
                wShortSum -= bAvg[i-cShortWidth];
            }
            if (i-cLongWidth >= 0) {
                wLongSum -= bAvg[i-cLongWidth];
            }
        }
    }
   
    /** Get the barcode read from the image.
     *
     * @return a String whose value is the barcode read from the image. Will
     * be null if no barcode was read (yet).
     */
    public String getCode() {
        return this.szBestCode;
    }
   
    /**
     * Get left top position of barcode.
     * @return the approximate column in the top row where the barcode lies.
     */
    public int getLeftApproxPos() {
        return this.cYLeft;
    }
   
    /**
     * Get the slope of the left edge of the barcode.
     * @return the approximate slope of the left edge of the barcode, multiplied by
     * 256.
     */
    public int getLeftApproxSlope() {
        return this.wSlopeLeft;
    }

    /**
     * Get the right top position of the barcode.
     * @return the approximate right edge of the barcode, in the top row of the image.
     */
    public int getRightApproxPos() {
        return this.cYRight;
    }
   
    /**
     * Get the slope of the right edge of the barcode.
     * @return the slope of the right side of the barcode.
     */
    public int getRightApproxSlope() {
        return this.wSlopeRight;
    }
   
    /** Returns true iff a barcode was read from the image.
     *
     * @return whether or not a barcode was read.
     */
    public boolean getSuccessful() {
        return this.szBestCode != null;
    }
   
    /**
     * Read the barcode. The image must be an 8-bit gray image. The other
     * parameters specify a cropping window. The barcode should almost fill
     * the cropping window. The barcode will be located and rectified in the
     * window. It is assumed to extend horizontally across the window. The
     * resulting barcode, if read, will be returned from getCode().
     * getSuccessful() can be queried to determine whether the barcode was
     * successfully read.
     * <p>
     * The image is cropped as all pixels whose position is greater than or
     * equal to the top or left, and less than the bottom and right.
     * @param image the input image.
     * @throws jjil.core.Error if the barcode rectangle is outside the image, or the input image is not
     * an RgbImage.
     */
    public void push(Image image) throws jjil.core.Error {
        int dTopLeftX = this.rectBarcode.getLeft();
        int dTopLeftY = this.rectBarcode.getTop();
        int cWidth = this.rectBarcode.getWidth();
        int cHeight = this.rectBarcode.getHeight();
       
        this.szBestCode = null;
        this.szBestName = null;
        this.wBestMatch = Integer.MIN_VALUE;
        if (dTopLeftX < 0 || dTopLeftX > image.getWidth() ||
            dTopLeftY < 0 || dTopLeftY > image.getHeight() ||
            cWidth < 0 || dTopLeftX + cWidth > image.getWidth() ||
            cHeight < 0 || dTopLeftY + cHeight > image.getHeight()) {
            throw new jjil.core.Error(
            jjil.core.Error.PACKAGE.ALGORITHM,
            jjil.algorithm.ErrorCodes.PARAMETER_OUT_OF_RANGE,
            "(" + dTopLeftX + "," + dTopLeftY + "," + cWidth + "," + cHeight + ")",
            image.toString(),
            null);
        }
        /* Convert the RGB input image to gray, crop, and detect edges. This
         * initializes imageCropped and imageEdge.
         */
        cropAndDetectEdges(image, dTopLeftX, dTopLeftY, cWidth, cHeight);
        /* Find the approximate left and right edges of the barcode. Sets
         * cYLeft, cYRight, wSlopeLeft, and wSlopeRight
         */
        if (!findBarcodeEdges()) {
            return;
        }

        /* Rectify the barcode image so the left and right edges are vertical
         */
        // rectifyBarcode();
        /* Find the exact left and right edges of the barcode in the rectified
         * image. Initializes dBestLeftPos and dBestRightPos.
         */
        // findPreciseBarcodeLimits();
        // if (this.dBestLeftPos == -1 || this.dBestRightPos == -1) {
        //     return;
        // }
        BarcodeReaderEan13 reader = new BarcodeReaderEan13();
        /*
        Gray8Crop gc = new Gray8Crop(dBestLeftPos,
            0,
            dBestRightPos - dBestLeftPos,
            this.imageRectified.getHeight());
        gc.push(this.imageRectified);
        Gray8Image imCropped = (Gray8Image) gc.getFront();
        {
             Debug debug = new Debug();
          Gray8Rgb g2r = new Gray8Rgb();
          g2r.push(this.imageCropped);
          debug.toFile((RgbImage) g2r.getFront(), "rectCropped.png");
         
        }
        */
        int wGoodness = reader.decode(
            this.imageCropped,
            this.cYLeft,
            this.cYRight,
            this.wSlopeLeft,
            this.wSlopeRight);
    if (reader.getCode() != null && wGoodness > this.wBestMatch) {
        this.szBestCode = reader.getCode();
        this.wBestMatch = wGoodness;
        this.szBestName = reader.getName();
    }
      
        /*
        for (int cInputWidth = dBestRightPos - dBestLeftPos - 2*cJitter;
            cInputWidth <= dBestRightPos - dBestLeftPos + 2*cJitter;
            cInputWidth++) {
            tryWidth(cInputWidth, cJitter, reader);
        }
        */
    }
   
    private void rectifyBarcode()
        throws jjil.core.Error
    {
        int cYLeft = Math.max(0, this.cYLeft - this.imageCropped.getWidth() / 8);

        int cClip = -this.wSlopeLeft * (this.imageCropped.getHeight() - 1) / 256;
        cYLeft = Math.max(cYLeft, cClip);
        int cYRight = Math.min(this.imageCropped.getWidth(),
                this.cYRight + this.imageCropped.getWidth() / 8);
        cClip = this.imageCropped.getWidth()
            - (this.imageCropped.getHeight() - 1) * this.wSlopeRight / 256;
        cYRight = Math.min(cYRight, cClip);
        int cTl = cYLeft;
        int cTr = cYRight;
        int cBl = cYLeft +
                (this.imageCropped.getHeight() - 1) * this.wSlopeLeft / 256;
        int cBr = cYRight +
                (this.imageCropped.getHeight() - 1) * this.wSlopeRight / 256;
        Gray8TrapWarp warp = new Gray8TrapWarp(0this.imageCropped.getHeight(),
                cTl,
                cTr,
                cBl,
                cBr);
        warp.push(this.imageCropped);
        Image imageResult = warp.getFront();
        if (!(imageResult instanceof Gray8Image)) {
            throw new jjil.core.Error(
            jjil.core.Error.PACKAGE.ALGORITHM,
            jjil.algorithm.ErrorCodes.IMAGE_NOT_GRAY8IMAGE,
            imageResult.toString(),
            null,
            null);
        }
        {
          Debug debug = new Debug();
          Gray8Rgb g2r = new Gray8Rgb();
          g2r.push(imageResult);
          debug.toFile((RgbImage) g2r.getFront(), "rectified.png"); //$NON-NLS-1$
        }
        this.imageRectified = (Gray8Image) imageResult;
    }
   
    /**
     * Set barcode rectangle.
     * @param rect The rectangle in which to look for a barcode.
     */
    public void setRect(Rect rect) {
      this.rectBarcode = rect;
    }
   
    private void tryWidth(int cInputWidth, int cJitter, BarcodeReader reader)
      throws jjil.core.Error
    {
        // find smallest cTargetWidth that is a cMultiple of reader.Width()
        // and larger than cInputWidth.
        // cMultiple is the smallest number of barcode cWidth's greater than the
        // input cWidth
        int cMultiple = ((cInputWidth + reader.getWidth() - 1) / reader.getWidth());
            // cTargetWidth is the width we'll stretch input cWidth to
        int cTargetWidth = reader.getWidth() * cMultiple;   
        Image imageStretched =
            cropAndStretchImage(
                    imageRectified,
                    dBestLeftPos-cJitter,
                    dBestRightPos+cJitter,
                    4,
                    cInputWidth,
                    cTargetWidth);
        {
          Gray8Rgb g2r = new Gray8Rgb();
          g2r.push(imageStretched);
          new Debug().toFile((RgbImage) g2r.getFront(), "stretched.png"); //$NON-NLS-1$
        }
        // now look for a barcode in the stretched rectangle at positions 0..2*cJitter-1
        // (in the input image) which must be scaled by cTargetWidth / cInputWidth in
        // the stretched image
        int wGoodness = reader.decode(
                        imageStretched);
        if (reader.getCode() != null && wGoodness > this.wBestMatch) {
            this.szBestCode = reader.getCode();
            this.wBestMatch = wGoodness;
            this.szBestName = reader.getName();
        }

    }
           
    private Vector getPointsFromZeroes(int[][] wZeroes, boolean oLeft) {
        Vector vResult = new Vector();
        for (int i=0; i<wZeroes.length; i++) {
            if (wZeroes[i] != null) {
                if (wZeroes[i].length > 5) {
                    if (oLeft) {
                      // on the left side of the barcode we are looking for a positive
                      // zero crossing, from dark to light
                      if (wZeroes[i][0] > 0) {
                            vResult.addElement(new Point(i, Math.abs(wZeroes[i][0]) / 256));                      
                      } else {
                        vResult.addElement(new Point(i, Math.abs(wZeroes[i][1]) / 256));
                      }
                    } else {
                      // on the right side of the barcode we are looking for a positive
                      // zero crossing, from dark to light
                      if (wZeroes[i][wZeroes[i].length-1] < 0) {
                            vResult.addElement(
                                    new Point(i, Math.abs(wZeroes[i][wZeroes[i].length-1]) / 256));
                      
                      } else {
                        vResult.addElement(
                            new Point(i, Math.abs(wZeroes[i][wZeroes[i].length-2]) / 256));
                       
                      }
                    }
                }
            }
        }
        return vResult;
    }
}
TOP

Related Classes of barcode.ReadBarcode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.