ALOS 卫星产品到 PNG 的转换问题(缺少旋转)
ALOS Satellite Product to PNG conversion issue (missing rotation)
我正在尝试使用 BEAM java API 导出 ALOS AVNIR-2 产品的 PNG quicklook。下图显示了产品的 RGB 预览,因为它出现在 beam 的 GUI 中。
如您所见,由于地理编码,图像不是直立的。我开发了一个非常简单的 java 程序来导出产品的快速浏览。
public static void main(String[] args) {
String[] rgbBandNames = new String[3];
rgbBandNames[0] = "radiance_3";
rgbBandNames[1] = "radiance_2";
rgbBandNames[2] = "radiance_1";
try {
//Product inputProduct = ProductIO.readProduct(args[0]);
Product inputProduct = ProductIO.readProduct("C:\nfsdata\VOL-ALAV2A152763430-O1B2R_U");
Band[] produtBands = inputProduct.getBands();
Band[] rgbBands = new Band[3];
int n = 0;
for (Band band : produtBands) {
if (band.getName().equals(rgbBandNames[0])) {
rgbBands[0] = band;
} else if (band.getName().equals(rgbBandNames[1])) {
rgbBands[1] = band;
} else if (band.getName().equals(rgbBandNames[2])) {
rgbBands[2] = band;
}
n = n + 1;
}
ImageInfo outImageInfo = ProductUtils.createImageInfo(rgbBands, true, ProgressMonitor.NULL);
BufferedImage outImage = ProductUtils.createRgbImage(rgbBands, outImageInfo, ProgressMonitor.NULL);
ImageIO.write(outImage, "PNG", new File(inputProduct.getName() + ".png"));
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
该程序可以运行,但我从中获得的每个 PNG 图像都是直立的 PNG 图像,如下所示。
现在,我知道不可能在 PNG 图像中包含地理编码信息。我只需要复制相同的 "rotation" 图像。
有什么想法吗?
我设法解决了我的问题。换句话说,我设法 从 ALOS AV2 O1B2R_U 产品中提取 quicklook,根据产品的地理编码信息旋转 (见下图)。
这是因为 ALOS AV2 O1B2R_U 产品的地理编码旋转 已经应用于栅格 。因此,为了成功导出 quicklook,必须从原生栅格中检索旋转并将其应用于输出图像。
为了将来参考,我想回顾一下我的解决方案并与社区分享。这是我的主要 class:
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)。在上面的代码中,使用了两个简单的图像处理函数:resize(...) 和 rotate(...),它们的定义见下文。
/**
* 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;
}
我正在尝试使用 BEAM java API 导出 ALOS AVNIR-2 产品的 PNG quicklook。下图显示了产品的 RGB 预览,因为它出现在 beam 的 GUI 中。
如您所见,由于地理编码,图像不是直立的。我开发了一个非常简单的 java 程序来导出产品的快速浏览。
public static void main(String[] args) {
String[] rgbBandNames = new String[3];
rgbBandNames[0] = "radiance_3";
rgbBandNames[1] = "radiance_2";
rgbBandNames[2] = "radiance_1";
try {
//Product inputProduct = ProductIO.readProduct(args[0]);
Product inputProduct = ProductIO.readProduct("C:\nfsdata\VOL-ALAV2A152763430-O1B2R_U");
Band[] produtBands = inputProduct.getBands();
Band[] rgbBands = new Band[3];
int n = 0;
for (Band band : produtBands) {
if (band.getName().equals(rgbBandNames[0])) {
rgbBands[0] = band;
} else if (band.getName().equals(rgbBandNames[1])) {
rgbBands[1] = band;
} else if (band.getName().equals(rgbBandNames[2])) {
rgbBands[2] = band;
}
n = n + 1;
}
ImageInfo outImageInfo = ProductUtils.createImageInfo(rgbBands, true, ProgressMonitor.NULL);
BufferedImage outImage = ProductUtils.createRgbImage(rgbBands, outImageInfo, ProgressMonitor.NULL);
ImageIO.write(outImage, "PNG", new File(inputProduct.getName() + ".png"));
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
该程序可以运行,但我从中获得的每个 PNG 图像都是直立的 PNG 图像,如下所示。
现在,我知道不可能在 PNG 图像中包含地理编码信息。我只需要复制相同的 "rotation" 图像。 有什么想法吗?
我设法解决了我的问题。换句话说,我设法 从 ALOS AV2 O1B2R_U 产品中提取 quicklook,根据产品的地理编码信息旋转 (见下图)。
这是因为 ALOS AV2 O1B2R_U 产品的地理编码旋转 已经应用于栅格 。因此,为了成功导出 quicklook,必须从原生栅格中检索旋转并将其应用于输出图像。 为了将来参考,我想回顾一下我的解决方案并与社区分享。这是我的主要 class:
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)。在上面的代码中,使用了两个简单的图像处理函数:resize(...) 和 rotate(...),它们的定义见下文。
/**
* 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;
}