/*
* Gray8SubImageGenerator.java
*
* Given a target image size and a horizontal and vertical offset
* generates a series of subimages within the input image,
* each subimage offset by an integral multiple of the
* offset with size equal to the target size and lying
* entirely within the original image. The offset of the
* subimage in the input image is given in the subimage class.
*
* Created on July 1, 2007, 1:51 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 <http://www.gnu.org/licenses/>.
*
*/
package jjil.algorithm;
import jjil.core.Error;
import jjil.core.Gray8Image;
import jjil.core.Gray8OffsetImage;
import jjil.core.Image;
import jjil.core.PipelineStage;
/**
* Generates sub images (cropped images positioned regularly across the input image)
* from an input Gray8Image. The subimages are of type Gray8OffsetImage which makes it
* possible to determine their location in the original input image.
* @author webb
*/
public class Gray8SubImageGenerator extends PipelineStage {
Gray8Image imageInput; // input image
int nHeight; // target height
int nHorizLimit = 0; // number of subimages generated horizontally
int nVertLimit = 0; // number of subimages generated vertically
int nHorizIndex = 0; // current subimiage index, horizontal
int nVertIndex = 0; // current subimage index, vertical
int nWidth; // target width
int nXOffset; // x offset multiple for subimages
int nYOffset; // y offset multiple for subimages
/**
* Creates a new instance of Gray8SubImageGenerator. The parameters specify the
* size and spacing of the subimages. For example (20,30,10,15) generates
* 20x30 subimages, spaced every 10 pixels horizontally and 15 pixels vertically.
* @param nWidth The width of the generated subimage.
* @param nHeight The height of the generated subimage.
* @param nXOffset The horizontal offset from one subimage to the next.
* @param nYOffset The vertical offset from one subimage to the next.
*/
public Gray8SubImageGenerator(int nWidth, int nHeight, int nXOffset, int nYOffset) {
this.nWidth = nWidth;
this.nHeight = nHeight;
this.nXOffset = nXOffset;
this.nYOffset = nYOffset;
// create an output image. We'll reuse this
// image, changing the contents and offset,
// for every Gray8OffsetImage we output.
super.imageOutput = new Gray8OffsetImage(
this.nWidth,
this.nHeight,
0,
0);
}
// We are done producing images when the last row is done
/**
* Returns true when no more subimages are available from the input image.
* isEmpty() should be called before getFront() to verify that subimages are available
* since it is an error to call getFront() when isEmpty() is true.
* @return true when no more subimages are available.
*/
public boolean isEmpty() {
return this.nVertIndex == this.nVertLimit;
}
// Return the next subimage and increment the indices
/**
* Returns the next subimage.
* @return a subimage within the input image, of type Gray8OffsetImage.
* @throws jjil.core.Error when there are no more subimages available (isEmpty() would return
* true.)
*/
public Image getFront() throws jjil.core.Error
{
// offset of first pixel of the subimage within the
// larget image.
int nHOffset = this.nXOffset * this.nHorizIndex;
int nVOffset = this.nYOffset * this.nVertIndex;
byte[] dataIn = this.imageInput.getData();
// reuse output image
// check to make sure nobody damaged it somehow
if (!(super.imageOutput instanceof Gray8OffsetImage)) {
throw new Error(
Error.PACKAGE.ALGORITHM,
ErrorCodes.IMAGE_NOT_GRAY8IMAGE,
imageOutput.toString(),
null,
null);
}
Gray8OffsetImage imageResult = (Gray8OffsetImage) super.imageOutput;
imageResult.setXOffset(nHOffset);
imageResult.setYOffset(nVOffset);
byte[] dataOut = imageResult.getData();
for (int i=0; i<this.nHeight; i++) {
int nVInLoc = i + nVOffset;
System.arraycopy(
dataIn,
nVInLoc*this.imageInput.getWidth() + nHOffset,
dataOut,
i*this.nWidth,
this.nWidth);
}
this.nHorizIndex ++;
if (this.nHorizIndex == this.nHorizLimit) {
this.nVertIndex ++;
this.nHorizIndex = 0;
}
return imageResult;
}
/**
* Reinitializes the subimage generator and prepares it to generate the first
* Gray8OffsetImage for the new input.
* @param image The new input image (which must be of type Gray8Image).
* @throws jjil.core.Error if image is not of type Gray8Image, or is too small
* (less than the size of the subimages we're supposed to
* be generating).
*/
public void push(Image image) throws jjil.core.Error {
if (!(image instanceof Gray8Image)) {
throw new Error(
Error.PACKAGE.ALGORITHM,
ErrorCodes.IMAGE_NOT_GRAY8IMAGE,
image.toString(),
null,
null);
}
if (image.getWidth() < this.nWidth || image.getHeight() < this.nHeight) {
throw new Error(
Error.PACKAGE.ALGORITHM,
ErrorCodes.IMAGE_TOO_SMALL,
image.toString(),
new Integer(this.nWidth).toString(),
new Integer(this.nHeight).toString());
}
this.imageInput = (Gray8Image) image;
// we want to find the largest integer l such that
// (l-1) * w + w < iw
// where l = computed limit on index
// w = subimage width or height
// iw = image width or height
// or l = iw / w (truncated)
// Java division truncates
this.nHorizLimit = (image.getWidth() - this.nWidth) / this.nXOffset;
this.nVertLimit = (image.getHeight() - this.nHeight) / this.nYOffset;
this.nHorizIndex = 0;
this.nVertIndex = 0;
}
}