// Catalano Imaging Library
// The Catalano Framework
//
// Copyright © Diego Catalano, 2012-2016
// diego.catalano at live.com
//
// Copyright © Andrew Kirillov, 2007-2008
// andrew.kirillov at gmail.com
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
package Catalano.Imaging.Tools;
import Catalano.Core.IntPoint;
import Catalano.Imaging.FastBitmap;
import Catalano.Imaging.IProcessImage;
import Catalano.Math.Geometry.QuadrilateralTransformationCalc;
import java.util.ArrayList;
import java.util.List;
/**
* Performs quadrilateral transformation of an area in a given source image.
*
* The class implements quadrilateral transformation algorithm, which allows to transform any quadrilateral from a given source image to a rectangular image.
* The idea of the algorithm is based on homogeneous transformation and its math is described by Paul Heckbert in his "Projective Mappings for Image Warping" paper.
*
* @author Diego Catalano
*/
public class QuadrilateralTransformation implements IProcessImage{
private boolean automaticSizeCalculaton = true;
private boolean useInterpolation = true;
private int newWidth;
private int newHeight;
private List sourceQuadrilateral;
/**
* Is Automatic calculation of destination image.
* @return True or false.
*/
public boolean isAutomaticSizeCalculaton() {
return automaticSizeCalculaton;
}
/**
* Set Automatic calculation of destination image
* @param automaticSizeCalculaton True or false.
*/
public void setAutomaticSizeCalculaton(boolean automaticSizeCalculaton) {
this.automaticSizeCalculaton = automaticSizeCalculaton;
if (automaticSizeCalculaton) CalculateDestinationSize();
}
/**
* Get Quadrilateral's corners in source image.
* @return Quadrilateral's corners.
*/
public List getSourceQuadrilateral() {
return sourceQuadrilateral;
}
/**
* Set Quadrilateral's corners in source image.
* @param sourceQuadrilateral Quadrilateral's corners.
*/
public void setSourceQuadrilateral(List sourceQuadrilateral) {
this.sourceQuadrilateral = sourceQuadrilateral;
if (automaticSizeCalculaton) CalculateDestinationSize();
}
/**
* Get New width.
* @return New width.
*/
public int getNewWidth() {
return newWidth;
}
/**
* Set New width.
* @param newWidth New width.
*/
public void setNewWidth(int newWidth) {
this.newWidth = newWidth;
if (!automaticSizeCalculaton) this.newWidth = Math.max( 1, newWidth );
}
/**
* Get New height.
* @return New height.
*/
public int getNewHeight() {
return newHeight;
}
/**
* Set New height.
* @param newHeight New height.
*/
public void setNewHeight(int newHeight) {
this.newHeight = newHeight;
if (!automaticSizeCalculaton) this.newHeight = Math.max(1, newHeight);
}
/**
* Specifies if bilinear interpolation should be used or not.
* @return True or false.
*/
public boolean isUseInterpolation() {
return useInterpolation;
}
/**
* Set if bilinear interpolation should be used or not.
* @param useInterpolation True or false.
*/
public void setUseInterpolation(boolean useInterpolation) {
this.useInterpolation = useInterpolation;
}
/**
* Initializes a new instance of the QuadrilateralTransformation class.
* @param sourceQuadrilateral Quadrilateral's corners.
*/
public QuadrilateralTransformation(List sourceQuadrilateral){
this.automaticSizeCalculaton = true;
this.sourceQuadrilateral = sourceQuadrilateral;
CalculateDestinationSize();
}
/**
* Initializes a new instance of the QuadrilateralTransformation class.
* @param sourceQuadrilateral Quadrilateral's corners.
* @param newWidth New width.
* @param newHeight New height.
*/
public QuadrilateralTransformation(List sourceQuadrilateral, int newWidth, int newHeight){
this.automaticSizeCalculaton = false;
this.sourceQuadrilateral = sourceQuadrilateral;
this.newWidth = newWidth;
this.newHeight = newHeight;
}
@Override
public FastBitmap ProcessImage(FastBitmap fastBitmap) {
FastBitmap dst = new FastBitmap(newWidth, newHeight, fastBitmap.getColorSpace());
int srcWidth = fastBitmap.getWidth();
int srcHeight = fastBitmap.getHeight();
int dstWidth = newWidth;
int dstHeight = newHeight;
ArrayList dstRect = new ArrayList( );
dstRect.add( new IntPoint( 0, 0 ) );
dstRect.add( new IntPoint( dstWidth - 1, 0 ) );
dstRect.add( new IntPoint( dstWidth - 1, dstHeight - 1 ) );
dstRect.add( new IntPoint( 0, dstHeight - 1 ) );
if(fastBitmap.getCoordinateSystem() == FastBitmap.CoordinateSystem.Matrix){
for (IntPoint p : sourceQuadrilateral) {
p.Swap();
}
}
// calculate tranformation matrix
double[][] matrix = QuadrilateralTransformationCalc.MapQuadToQuad( dstRect, sourceQuadrilateral );
if(fastBitmap.getCoordinateSystem() == FastBitmap.CoordinateSystem.Matrix){
for (IntPoint p : sourceQuadrilateral) {
p.Swap();
}
}
if(!useInterpolation){
if (fastBitmap.isRGB()){
for (int i = 0; i < dstHeight; i++) {
for (int j = 0; j < dstWidth; j++) {
double factor = matrix[2][0] * j + matrix[2][1] * i + matrix[2][2];
double srcX = ( matrix[0][0] * j + matrix[0][1] * i + matrix[0][2] ) / factor;
double srcY = ( matrix[1][0] * j + matrix[1][1] * i + matrix[1][2] ) / factor;
if ( ( srcX >= 0 ) && ( srcY >= 0 ) && ( srcX < srcWidth ) && ( srcY < srcHeight ) )
{
int r = fastBitmap.getRed((int)srcY, (int)srcX);
int g = fastBitmap.getGreen((int)srcY, (int)srcX);
int b = fastBitmap.getBlue((int)srcY, (int)srcX);
dst.setRGB(i, j, r, g, b);
}
}
}
}
if (fastBitmap.isGrayscale()){
for (int i = 0; i < dstHeight; i++) {
for (int j = 0; j < dstWidth; j++) {
double factor = matrix[2][0] * j + matrix[2][1] * i + matrix[2][2];
double srcX = ( matrix[0][0] * j + matrix[0][1] * i + matrix[0][2] ) / factor;
double srcY = ( matrix[1][0] * j + matrix[1][1] * i + matrix[1][2] ) / factor;
if ( ( srcX >= 0 ) && ( srcY >= 0 ) && ( srcX < srcWidth ) && ( srcY < srcHeight ) )
{
int g = fastBitmap.getGray((int)srcY, (int)srcX);
dst.setGray(i, j, g);
}
}
}
}
}
else{
if (fastBitmap.isRGB()){
int srcWidthM1 = srcWidth - 1;
int srcHeightM1 = srcHeight - 1;
// coordinates of source points
double dx1, dy1, dx2, dy2;
int sx1, sy1, sx2, sy2;
int p1r, p2r, p3r, p4r;
int p1g, p2g, p3g, p4g;
int p1b, p2b, p3b, p4b;
for (int i = 0; i < dstHeight; i++) {
for (int j = 0; j < dstWidth; j++) {
double factor = matrix[2][0] * j + matrix[2][1] * i + matrix[2][2];
double srcX = ( matrix[0][0] * j + matrix[0][1] * i + matrix[0][2] ) / factor;
double srcY = ( matrix[1][0] * j + matrix[1][1] * i + matrix[1][2] ) / factor;
if ( ( srcX >= 0 ) && ( srcY >= 0 ) && ( srcX < srcWidth ) && ( srcY < srcHeight ) ){
sx1 = (int) srcX;
sx2 = ( sx1 == srcWidthM1 ) ? sx1 : sx1 + 1;
dx1 = srcX - sx1;
dx2 = 1.0 - dx1;
sy1 = (int) srcY;
sy2 = ( sy1 == srcHeightM1 ) ? sy1 : sy1 + 1;
dy1 = srcY - sy1;
dy2 = 1.0 - dy1;
// get four points in Red channel
p1r = fastBitmap.getRed(sy1, sx1);
p2r = fastBitmap.getRed(sy1, sx2);
p3r = fastBitmap.getRed(sy2, sx1);
p4r = fastBitmap.getRed(sy2, sx2);
// get four points in Green channel
p1g = fastBitmap.getGreen(sy1, sx1);
p2g = fastBitmap.getGreen(sy1, sx2);
p3g = fastBitmap.getGreen(sy2, sx1);
p4g = fastBitmap.getGreen(sy2, sx2);
// get four points in Blue channel
p1b = fastBitmap.getBlue(sy1, sx1);
p2b = fastBitmap.getBlue(sy1, sx2);
p3b = fastBitmap.getBlue(sy2, sx1);
p4b = fastBitmap.getBlue(sy2, sx2);
int r = (int)(dy2 * (dx2 * (p1r) + dx1 * (p2r)) + dy1 * (dx2 * (p3r) + dx1 * (p4r)));
int g = (int)(dy2 * (dx2 * (p1g) + dx1 * (p2g)) + dy1 * (dx2 * (p3g) + dx1 * (p4g)));
int b = (int)(dy2 * (dx2 * (p1b) + dx1 * (p2b)) + dy1 * (dx2 * (p3b) + dx1 * (p4b)));
dst.setRGB(i, j, r, g, b);
}
}
}
}
if (fastBitmap.isGrayscale()){
int srcWidthM1 = srcWidth - 1;
int srcHeightM1 = srcHeight - 1;
// coordinates of source points
double dx1, dy1, dx2, dy2;
int sx1, sy1, sx2, sy2;
int p1, p2, p3, p4;
for (int i = 0; i < dstHeight; i++) {
for (int j = 0; j < dstWidth; j++) {
double factor = matrix[2][0] * j + matrix[2][1] * i + matrix[2][2];
double srcX = ( matrix[0][0] * j + matrix[0][1] * i + matrix[0][2] ) / factor;
double srcY = ( matrix[1][0] * j + matrix[1][1] * i + matrix[1][2] ) / factor;
if ( ( srcX >= 0 ) && ( srcY >= 0 ) && ( srcX < srcWidth ) && ( srcY < srcHeight ) ){
sx1 = (int) srcX;
sx2 = ( sx1 == srcWidthM1 ) ? sx1 : sx1 + 1;
dx1 = srcX - sx1;
dx2 = 1.0 - dx1;
sy1 = (int) srcY;
sy2 = ( sy1 == srcHeightM1 ) ? sy1 : sy1 + 1;
dy1 = srcY - sy1;
dy2 = 1.0 - dy1;
// get four points
p1 = fastBitmap.getGray(sy1, sx1);
p2 = fastBitmap.getGray(sy1, sx2);
p3 = fastBitmap.getGray(sy2, sx1);
p4 = fastBitmap.getGray(sy2, sx2);
int g = (int)(dy2 * (dx2 * (p1) + dx1 * (p2)) + dy1 * (dx2 * (p3) + dx1 * (p4)));
dst.setGray(i, j, g);
}
}
}
}
}
return dst;
}
// Calculates size of destination image
private void CalculateDestinationSize( ){
if ( sourceQuadrilateral == null )
throw new IllegalArgumentException( "Source quadrilateral was not set." );
newWidth = (int) Math.max( sourceQuadrilateral.get(0).DistanceTo( sourceQuadrilateral.get(1) ),
sourceQuadrilateral.get(2).DistanceTo( sourceQuadrilateral.get(3) ) );
newHeight = (int) Math.max( sourceQuadrilateral.get(1).DistanceTo( sourceQuadrilateral.get(2) ),
sourceQuadrilateral.get(3).DistanceTo( sourceQuadrilateral.get(0) ) );
}
}
Ads help maintain this website.