HDR(High Dynamic Range,高动态范围),由于目前无论是相机还是显示器,对图像亮度范围都限定在0~255之间,即256个亮度级别,但在自然界中,刺眼的阳光和微弱的星光,可能有成千上万倍的亮度差异。如果要在同一张照片上同时呈现两个亮度差异较大的物体,那么实际情况是,要么其中一个亮度过低,或者另一个过曝。根据不同应用场景,HDR有不同处理方式。
其中一种方法为,采用不同曝光参数(ISO、快门速度)拍摄同一场景照片,对每张照片相同位置像素值求和,计算平均值,再将均值赋值给对应像素;另一种类似的处理方法为,相机采用较为平衡的曝光参数拍摄一张照片,根据这张照片分别按照亮度增强一定比例和亮度降低一定比例生成两张影像,将三张影像按照前面一种方法进行处理。
当然在实际处理同一位置拍摄多张影像进行HDR时,需要考虑抖动等外界因素造成的图像错位,需要引入特征提取与匹配及裁剪影像。
简单的demo实现代码:
public class Hdr {
/**
* HDR实现 不同曝光参数的几张照片实现影像画质增强
*
* @param images
* @return
*/
public BufferedImage hdrByImages(BufferedImage[] images) {
int width = images[0].getWidth();
int height = images[0].getHeight();
BufferedImage resultImage = new BufferedImage(width, height, images[0].getType());
int length = width * height;
int pics = images.length;
int[] tempR = new int[length];
int[] tempG = new int[length];
int[] tempB = new int[length];
// 取出每张照片对应位置像素值
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int index = i * height + j;
for (int k = 0; k < pics; k++) {
int rgb = images[k].getRGB(i, j);
tempR[index] += (rgb >> 16) & 0xff;
tempG[index] += (rgb >> 8) & 0xff;
tempB[index] += rgb & 0xff;
}
}
}
// 求取均值,将均值作为最终结果输出
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int index = i * height + j;
int r = tempR[index] / pics;
int g = tempG[index] / pics;
int b = tempB[index] / pics;
int rgb = (255 & 0xff) << 24 | (r & 0xff) << 16 | (g & 0xff) << 8 | (b & 0xff);
resultImage.setRGB(i, j, rgb);
}
}
return resultImage;
}
/**
* 用一张照片进行HDR计算
*
* @param image
* @param lightParam
* 提高亮度参数
* @param darkParam
* 降低亮度参数
* @return
*/
public BufferedImage hdrByimage(BufferedImage image, double lightParam, double darkParam) {
BufferedImage[] images = new BufferedImage[3];
images[0] = image;
images[1] = brightnessAdjustment(image, lightParam);
images[2] = brightnessAdjustment(image, darkParam);
BufferedImage result = hdrByImages(images);
return result;
}
// 亮度调整
public BufferedImage brightnessAdjustment(BufferedImage image, double param) {
BufferedImage resultImage = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
double R, G, B;
for (int i = 0; i < image.getWidth(); i++) {
for (int j = 0; j < image.getHeight(); j++) {
int rgb = image.getRGB(i, j);
R = (((rgb >> 16) & 0xff) * param);
G = (((rgb >> 8) & 0xff) * param);
B = ((rgb & 0xff) * param);
rgb = ((255 & 0xff) << 24) | ((clamp((int) R) & 0xff) << 16) | ((clamp((int) G) & 0xff) << 8)
| ((clamp((int) B) & 0xff));
resultImage.setRGB(i, j, rgb);
}
}
return resultImage;
}
private int clamp(int i) {
if (i > 255) {
return 255;
}
if (i < 0) {
return 0;
} else {
return i;
}
}
// 读取文件夹下多张影像
public BufferedImage[] readImages(File input) throws Exception {
String[] files = input.list();
BufferedImage[] images = new BufferedImage[files.length];
for (int i = 0; i < files.length; i++) {
String in = files[i];
File file = new File(input.toString() + "/" + in);
images[i] = ImageIO.read(file);
}
return images;
}
//测试
public static void main(String[] args) throws Exception {
File input = new File("C:/Users/admin/Desktop/test/HDR");
BufferedImage[] images = new Hdr().readImages(input);
BufferedImage result = new Hdr().hdrByImages(images);
File output = new File("C:/Users/admin/Desktop/test/HDR/result.jpg");
ImageIO.write(result, "jpg", output);
}
}
三张不同曝光参数的测试图像:
运行结果: