All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.ojil.algorithm.Gray8DetectHaarMultiScale Maven / Gradle / Ivy

There is a newer version: 0.0.11
Show newest version
package com.github.ojil.algorithm;

/*
 * Gray8DetectHaarMultiScale.java
 *
 * Created on August 19, 2007, 7:33 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 *
 * Copyright 2007 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 .
 *
 */
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import com.github.ojil.core.Gray8Image;
import com.github.ojil.core.Gray8MaskedImage;
import com.github.ojil.core.Gray8OffsetImage;
import com.github.ojil.core.Image;
import com.github.ojil.core.ImageError;
import com.github.ojil.core.PipelineStage;

/**
 * DetectHaar applies a Haar cascade at multiple locations and multiple scales
 * to an input Gray8Image. The result is a mask with the masked
 * (non-Byte.MIN_VALUE) locations indicating the areas where the feature was
 * detected.
* The Haar cascade is applied at multiple scales, starting with the coarsest * scale, and working down to the finest scale. At each scale, the cascade is * applied to subimages spread across the image. If the cascade detects a * feature, the area of the mask corresponding to that subimage is set to * Byte.MAX_VALUE. When a subimage is to be tested, the mask is first examined * to see if the central pixel in the mask area corresponding to that subimage * is masked. If it is, the subimage is skipped. When transitioning to a finer * scale, the mask is stretched to the new size. This results in areas where * features have been detected at a coarser scale not being re-searched at a * finer scale.
* Gray8DetectHaarMultiScale is structured as a pipeline stage so push'ing an * image results in a new mask being available on getFront. The mask can be * further processed by doing connected component detection to determine the * feature characteristics, or the mask can be displayed in an overlay on the * original image to show the feature areas. * * @author webb */ public class Gray8DetectHaarMultiScale extends PipelineStage { private final HaarClassifierCascade hcc; // maximum scale is the largest factor the image is divided by private int nMaxScale = 10; // minimum scale is the smallest factor the image is divided by private int nMinScale = 5; // scale change is the change in scale from one search to the next // times 256 private final int nScaleChange = (12 * 256) / 10; /** * Creates a new instance of Gray8DetectHaarMultiScale. The scale parameters * correspond to the size of a square area in the original input image that * are averaged to create a single pixel in the image used for detection. A * scale factor of 1 would do detection at full image resolution. * * @param is * Input stream containing the Haar cascade. This input stream is * created by the Haar2J2me program (run on a PC) from a Haar * cascade that has been trained using the OpenCV. See * {http://sourceforge.net/projects/opencv} for more information * about the OpenCV. The Haar2J2me program should be available * wherever you got this code from. * @param nMinScale * Minimum (finest) scale at which features will be detected. * @param nMaxScale * Maximum (coarsest) scale at which features will be detected. * @throws ImageError * if there is an error in the input file. * @throws java.io.IOException * if there is an I/O error reading the input file. */ public Gray8DetectHaarMultiScale(final InputStream is, final int nMinScale, final int nMaxScale) throws ImageError, IOException { this.nMinScale = nMinScale; this.nMaxScale = nMaxScale; // load Haar classifier cascade final InputStreamReader isr = new InputStreamReader(is); hcc = HaarClassifierCascade.fromStream(isr); } /** * Apply multi-scale Haar cascade and prepare a mask image showing where * features were detected. * * @param image * Input Gray8Image. * @throws ImageError * if the input is not a Gray8Image or is too small. */ @Override public void push(final Image image) throws ImageError { Gray8Image imGray; if (image instanceof Gray8Image) { imGray = (Gray8Image) image; } else { throw new ImageError(ImageError.PACKAGE.ALGORITHM, AlgorithmErrorCodes.IMAGE_NOT_GRAY8IMAGE, image.toString(), null, null); } if ((image.getWidth() < hcc.getWidth()) || (image.getHeight() < hcc.getHeight())) { throw new ImageError(ImageError.PACKAGE.ALGORITHM, AlgorithmErrorCodes.IMAGE_TOO_SMALL, image.toString(), hcc.toString(), null); } int nScale = Math.min(nMaxScale, Math.min(image.getWidth() / hcc.getWidth(), image.getHeight() / hcc.getHeight())); // Zero the mask Gray8Image imMask = new Gray8Image<>(1, 1, Byte.MIN_VALUE); while (nScale >= nMinScale) { // shrink the input image final int nTargetWidth = imGray.getWidth() / nScale; final int nTargetHeight = imGray.getHeight() / nScale; final Gray8Shrink gs = new Gray8Shrink(nTargetWidth, nTargetHeight); gs.push(imGray); final Gray8Image imShrunk = (Gray8Image) gs.getFront(); // scale the mask to the new size final Gray8RectStretch grs = new Gray8RectStretch(nTargetWidth, nTargetHeight); grs.push(imMask); imMask = (Gray8Image) grs.getFront(); // combine the image and mask to make a masked image final Gray8MaskedImage gmi = new Gray8MaskedImage<>(imShrunk, imMask); // pass the masked image to a subimage generator final MaskedGray8SubImgGen mgsi = new MaskedGray8SubImgGen(hcc.getWidth(), hcc.getHeight(), Math.max(1, gmi.getWidth() / 30), Math.max(1, gmi.getHeight() / 30)); mgsi.push(gmi); // now run Haar detection on each scaled image int nxLastFound = -hcc.getWidth(); int nyLastFound = -hcc.getHeight(); while (!mgsi.isEmpty()) { final Gray8OffsetImage imSub = (Gray8OffsetImage) mgsi.getFront(); // if we've found a feature recently we skip forward until // we're outside the masked region. There's no point rerunning // the detector if ((imSub.getXOffset() > (nxLastFound + hcc.getWidth())) && (imSub.getYOffset() > (nyLastFound + hcc.getHeight()))) { if (hcc.eval(imSub)) { // Found something. nxLastFound = imSub.getXOffset(); nyLastFound = imSub.getYOffset(); // assign Byte.MAX_VALUE to the feature area so we don't // search it again final Gray8Rect gr = new Gray8Rect(nxLastFound, nyLastFound, hcc.getWidth(), hcc.getHeight(), Byte.MAX_VALUE); gr.push(imMask); imMask = (Gray8Image) gr.getFront(); } } } nScale = (nScale * 256) / nScaleChange; } // Stretch imMask to original image size; this is the result final Gray8RectStretch grs = new Gray8RectStretch(image.getWidth(), image.getHeight()); grs.push(imMask); super.setOutput(grs.getFront()); } /** * Set minimum and maximum scale. * * @param nMinScale * The finest scale -- a scale factor of 1 corresponds to the * full image resolution. * @param nMaxScale * The coarsest scale. A scale factor equal to the image width * (for a square image) would mean the entire image is reduced to * a single pixel.
* Note. The maximum scale actually used is the maximum of * this number and the scale which would reduce the image size to * the smallest size that the image used in the Haar cascade * would fit inside. */ public void setScale(final int nMinScale, final int nMaxScale) { this.nMinScale = nMinScale; this.nMaxScale = nMaxScale; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy