Мне удалось решить мою проблему. Другими словами, мне удалось: извлечь quicklook из продукта ALOS AV2 O1B2R_U, повернутого в соответствии с информацией о геокодировании продукта (см. Изображение ниже).
Причина этого заключается в том, что продукты АЛОС AV2 O1B2R_U есть вращение геокодирования уже применяется к растру. Как следствие, для того, чтобы успешно экспортировать quicklook, ротация должна быть получена из собственного растра и применена к выходному изображению. В будущем, я хотел бы напомнить и поделиться своим решением с сообществом. Это мой основной класс:
import com.bc.ceres.core.ProgressMonitor;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.esa.beam.framework.dataio.ProductIO;
import org.esa.beam.framework.datamodel.Band;
import org.esa.beam.framework.datamodel.ImageInfo;
import org.esa.beam.framework.datamodel.MapGeoCoding;
import org.esa.beam.framework.datamodel.Product;
import org.esa.beam.util.ProductUtils;
public static void main(String[] args) throws IOException {
String inputProductPath = "path\to\input\product";
String outputProductPath = "path\to\output\image";
// Read the source product.
Product inputProduct = ProductIO.readProduct(inputProductPath);
// Extract the RGB bands.
String[] bandNames = new String[3];
Band[] bandData = new Band[3];
bandNames[0] = "radiance_3";
bandNames[1] = "radiance_2";
bandNames[2] = "radiance_1";
for (Band band : inputProduct.getBands()) {
for (int i = 0; i < bandNames.length; i++) {
if (band.getName().equalsIgnoreCase(bandNames[ i ])) {
bandData[ i ] = band;
}
}
}
// Generate quicklook image.
ImageInfo outImageInfo = ProductUtils.createImageInfo(bandData, true, ProgressMonitor.NULL);
BufferedImage outImage = ProductUtils.createRgbImage(bandData, outImageInfo, ProgressMonitor.NULL);
outImage = resize(outImage, WIDTH, 1200);
// Extract the orientation.
double orientation;
if (inputProduct.getGeoCoding() != null) {
orientation = -((MapGeoCoding) inputProduct.getGeoCoding()).getMapInfo().getOrientation();
} else {
orientation = 0.0;
}
outImage = rotate(outImage, orientation);
// Write image.
ImageIO.write(outImage, "PNG", new File(outputProductPath));
}
После того, как угол поворота QuickLook был извлечен из продукта источника (см код выше), то он должен быть применен к выходному изображению (BufferedImage). В приведенном выше коде используются две простые функции манипуляции изображениями: изменение размера (...) и поворот (...), см. Ниже их определение.
/**
* Resizes the image {@code tgtImage} by setting one of its dimensions
* (width or height, specified via {@code tgtDimension}) to {@code tgtSize}
* and dynamically calculating the other one in order to preserve the aspect
* ratio.
*
* @param tgtImage The image to be resized.
* @param tgtDimension The selected dimension: {@code ImageUtil.WIDTH} or
* {@code ImageUtil.WIDTH}.
* @param tgtSize The new value for the selected dimension.
*
* @return The resized image.
*/
public static BufferedImage resize(BufferedImage tgtImage, short tgtDimension, int tgtSize) {
int newWidth = 0, newHeight = 0;
if (HEIGHT == tgtDimension) {
newHeight = tgtSize;
newWidth = (tgtImage.getWidth() * tgtSize)/tgtImage.getHeight();
} else {
newHeight = (tgtImage.getHeight() * tgtSize)/tgtImage.getWidth();
newWidth = tgtSize;
}
Image tmp = tgtImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
BufferedImage outImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGemoticon;
Graphics2D g2d = outImage.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return outImage;
}
/**
* Rotates the image {@code tgtImage} by {@code tgtAngle} degrees clockwise.
*
* @param tgtImage The image to be rotated.
* @param tgtAngle The rotation angle (expressed in degrees).
*
* @return The resized image.
*/
public static BufferedImage rotate(BufferedImage tgtImage, double tgtAngle) {
int w = tgtImage.getWidth();
int h = tgtImage.getHeight();
AffineTransform t = new AffineTransform();
t.setToRotation(Math.toRadians(tgtAngle), w/2d, h/2d);
Point[] points = {
new Point(0, 0),
new Point(w, 0),
new Point(w, h),
new Point(0, h)
};
// Transform to destination rectangle.
t.transform(points, 0, points, 0, 4);
// Get destination rectangle bounding box
Point min = new Point(points[0]);
Point max = new Point(points[0]);
for (int i = 1, n = points.length; i < n; i++) {
Point p = points[ i ];
double pX = p.getX(), pY = p.getY();
// Update min/max x
if (pX < min.getX()) {
min.setLocation(pX, min.getY());
}
if (pX > max.getX()) {
max.setLocation(pX, max.getY());
}
// Update min/max y
if (pY < min.getY()) {
min.setLocation(min.getX(), pY);
}
if (pY > max.getY()) {
max.setLocation(max.getX(), pY);
}
}
// Determine new width, height
w = (int) (max.getX() - min.getX());
h = (int) (max.getY() - min.getY());
// Determine required translation
double tx = min.getX();
double ty = min.getY();
// Append required translation
AffineTransform translation = new AffineTransform();
translation.translate(-tx, -ty);
t.preConcatenate(translation);
AffineTransformOp op = new AffineTransformOp(t, null);
BufferedImage outImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGemoticon;
op.filter(tgtImage, outImage);
return outImage;
}