当前位置: 首页 > news >正文

kotlin - 显示HDR图(heic格式),使用GainMap算法,速度从5秒提升到0.6秒

kotlin - 显示HDR图(heic格式),使用GainMap算法,速度从5秒提升到0.6秒

class HdrImageDecoderActivity : AppCompatActivity() , View.OnClickListener{override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.hdr_image_decoder_main)findViewById<Button>(R.id.hdr_show_btn1).setOnClickListener(this)findViewById<Button>(R.id.hdr_show_btn2).setOnClickListener(this)}override fun onClick(v: View?) {v?:returnwhen(v.id){R.id.hdr_show_btn1 -> {show1()}R.id.hdr_show_btn2 -> {show2()}}}private fun show2(){LogUtils.i("AAA", "11111")val resultImageView = findViewById<ImageView>(R.id.hdr_show_result)val resultImageView2 = findViewById<ImageView>(R.id.hdr_show_result2)//val filePath = "sdcard/DCIM/AAA/IMG_20251101_154711.HEIC"val filePath = "sdcard/DCIM/Camera/IMG_20251101_154711.HEIC"//val filePath = "sdcard/DCIM/Camera/IMG_20251026_100717.HEIC"//val filePath = "sdcard/DCIM/Camera/IMG_20251102_162507.HEIC"val options = BitmapFactory.Options()options.inSampleSize = 2val bitmap = BitmapFactory.decodeFile(filePath, options)resultImageView.setImageBitmap(bitmap)LogUtils.i("BBB", "111")val gainMapResult = GainMapDecoder.decodeGainMap(filePath)LogUtils.i("BBB", "222")if(gainMapResult != null){val bitmap2 = GainMapProcessor.applyGainMapConservative(gainMapResult.baseImage, gainMapResult.gainMap)LogUtils.i("BBB", "333")resultImageView2.setImageBitmap(bitmap2)}/*val bitmap2 = HDRHelper.loadProXdrImage(filePath)resultImageView2.setImageBitmap(bitmap2)LogUtils.i("AAA", "33333")*/}private fun show1(){val resultImageView = findViewById<ImageView>(R.id.hdr_show_result)val resultImageView2 = findViewById<ImageView>(R.id.hdr_show_result2)val filePath = "sdcard/DCIM/Camera/IMG_20251026_111918.HEIC"LogUtils.i("AAA", "exist = " + File(filePath).exists())//HeifLoader.loadHeifImage(filePath, resultImageView)val bitmap = HDRHelper.loadProXdrImage(filePath)resultImageView.setImageBitmap(bitmap)val options = BitmapFactory.Options()//options.inSampleSize = 20val bitmap2 = BitmapFactory.decodeFile(filePath)resultImageView2.setImageBitmap(bitmap2)}
}

package com.example.androidkotlindemo2.hdr;/*** Author : wn* Email : maoning20080809@163.com* Date : 2025/11/1 14:47* Description :*/import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.DisplayMetrics;
import android.util.Log;import androidx.exifinterface.media.ExifInterface;import com.example.androidkotlindemo2.utils.LogUtils;public class GainMapDecoder {private static final String TAG = "GainMapDecoder";public static class GainMapResult {public Bitmap baseImage;public Bitmap gainMap;public float gamma;public float hdrCapacityMin;public float hdrCapacityMax;public float offsetSdr;public float offsetHdr;public GainMapResult() {// 默认值this.gamma = 1.0f;this.hdrCapacityMin = 0.0f;this.hdrCapacityMax = 1.0f;this.offsetSdr = 0.0f;this.offsetHdr = 0.0f;}}public static GainMapResult decodeGainMap(String imagePath) {GainMapResult result = new GainMapResult();try {// 第一步:获取图片尺寸而不加载到内存BitmapFactory.Options sizeOptions = new BitmapFactory.Options();sizeOptions.inJustDecodeBounds = true;BitmapFactory.decodeFile(imagePath, sizeOptions);int imageWidth = sizeOptions.outWidth;int imageHeight = sizeOptions.outHeight;// 第二步:根据屏幕宽度计算合适的采样率DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();int screenWidth = displayMetrics.widthPixels ;// 计算采样率,确保解码后的图片宽度接近屏幕宽度int inSampleSize = calculateInSampleSize(imageWidth , screenWidth);//int inSampleSize = 2;LogUtils.Companion.e("BBB", "decodeGainMap imageWidth = " + imageWidth +" , imageHeight = " + imageHeight +" , inSampleSize = " + inSampleSize +" ,screenWidth = " + screenWidth);// 第三步:使用计算出的采样率解码图片BitmapFactory.Options decodeOptions = new BitmapFactory.Options();decodeOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;decodeOptions.inSampleSize = inSampleSize;result.baseImage = BitmapFactory.decodeFile(imagePath, decodeOptions);LogUtils.Companion.e("BBB", "decodeGainMap result imageWidth = " + result.baseImage.getWidth() +" , imageHeight = " + result.baseImage.getHeight());if (result.baseImage == null) {Log.e(TAG, "Failed to decode base image");return null;}// 尝试从EXIF数据读取GainMap信息ExifInterface exif = new ExifInterface(imagePath);// 检查是否存在GainMap相关标签boolean hasGainMap = hasGainMapMetadata(exif);if (hasGainMap || true) {Log.d(TAG, "GainMap metadata found, attempting to decode...");result.gainMap = extractGainMapFromExif(exif);readGainMapParameters(exif, result);} else {Log.d(TAG, "No GainMap metadata found, using base image only");}} catch (Exception e) {Log.e(TAG, "Error decoding GainMap: " + e.getMessage(), e);// 出错时至少返回基础图像if (result.baseImage == null) {try {result.baseImage = BitmapFactory.decodeFile(imagePath);} catch (Exception ex) {Log.e(TAG, "Failed to decode base image as fallback", ex);}}}return result;}/*** 计算合适的采样率* @param imageWidth 图片原始宽度* @param targetWidth 目标宽度(屏幕宽度)* @return 采样率,总是2的幂次方*/private static int calculateInSampleSize(int imageWidth, int targetWidth) {int inSampleSize = 1;if (imageWidth > targetWidth) {// 计算理论采样率float ratio = (float) imageWidth / targetWidth;inSampleSize = Math.round(ratio);// 确保采样率是2的幂次方(BitmapFactory的要求)inSampleSize = roundToPowerOfTwo(inSampleSize);}// 设置最小和最大采样率限制inSampleSize = Math.max(1, inSampleSize);inSampleSize = Math.min(16, inSampleSize); // 防止采样率过大return inSampleSize;}/*** 将数值向上取整到最近的2的幂次方* 例如:3→4, 5→8, 9→16*/private static int roundToPowerOfTwo(int value) {int power = 1;while (power < value) {power *= 2;}return power;}private static boolean hasGainMapMetadata(ExifInterface exif) {// 检查常见的GainMap相关EXIF标签String makerNote = exif.getAttribute(ExifInterface.TAG_MAKER_NOTE);String userComment = exif.getAttribute(ExifInterface.TAG_USER_COMMENT);LogUtils.Companion.e("BBB", "hasGainMapMetadata makerNote = " + makerNote +" , userComment = " + userComment);return (makerNote != null && makerNote.contains("GainMap")) ||(userComment != null && userComment.contains("GainMap")) ||exif.getAttribute("GainMapVersion") != null;}private static Bitmap extractGainMapFromExif(ExifInterface exif) {try {// 尝试从MakerNote或其他EXIF字段提取GainMap数据byte[] gainMapData = exif.getThumbnail();if (gainMapData != null && gainMapData.length > 0) {return BitmapFactory.decodeByteArray(gainMapData, 0, gainMapData.length);}} catch (Exception e) {Log.e(TAG, "Failed to extract GainMap from EXIF", e);}return null;}private static void readGainMapParameters(ExifInterface exif, GainMapResult result) {try {// 读取GainMap参数String gamma = exif.getAttribute("GainMapGamma");String hdrMin = exif.getAttribute("GainMapHDRMin");String hdrMax = exif.getAttribute("GainMapHDRMax");String offsetSdr = exif.getAttribute("GainMapOffsetSDR");String offsetHdr = exif.getAttribute("GainMapOffsetHDR");if (gamma != null) result.gamma = Float.parseFloat(gamma);if (hdrMin != null) result.hdrCapacityMin = Float.parseFloat(hdrMin);if (hdrMax != null) result.hdrCapacityMax = Float.parseFloat(hdrMax);if (offsetSdr != null) result.offsetSdr = Float.parseFloat(offsetSdr);if (offsetHdr != null) result.offsetHdr = Float.parseFloat(offsetHdr);} catch (Exception e) {Log.e(TAG, "Error reading GainMap parameters", e);}}}

package com.example.androidkotlindemo2.hdr;import android.graphics.Bitmap;import com.example.androidkotlindemo2.utils.LogUtils;/*** Author : wn* Email : maoning20080809@163.com* Date : 2025/11/1 14:43* Description :*/public class GainMapProcessor {private static final String TAG = "GainMapProcessor";/*** 应用GainMap算法 - 优化曝光版本*/public static Bitmap applyGainMapAlgorithm(Bitmap baseImage, Bitmap gainMap,float gamma, float hdrCapacityMin,float hdrCapacityMax, float offsetSdr,float offsetHdr) {if (baseImage == null) return null;if (gainMap == null) return baseImage;int width = baseImage.getWidth();int height = baseImage.getHeight();LogUtils.Companion.d("BBB", "applyGainMapAlgorithm " + baseImage.getWidth() +" , " + baseImage.getHeight() +" , " + gainMap.getWidth() +" , " + gainMap.getHeight());// 调整GainMap尺寸以匹配基础图像Bitmap scaledGainMap = Bitmap.createScaledBitmap(gainMap, width, height, true);// 创建结果BitmapBitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 应用GainMap算法applyGainMapPixelsOptimized(baseImage, scaledGainMap, result,gamma, hdrCapacityMin, hdrCapacityMax,offsetSdr, offsetHdr);if (scaledGainMap != gainMap) {scaledGainMap.recycle();}return result;}private static void applyGainMapPixelsOptimized(Bitmap baseImage, Bitmap gainMap,Bitmap result, float gamma,float hdrCapacityMin, float hdrCapacityMax,float offsetSdr, float offsetHdr) {int width = baseImage.getWidth();int height = baseImage.getHeight();int[] basePixels = new int[width * height];int[] gainPixels = new int[width * height];int[] resultPixels = new int[width * height];// 获取像素数据baseImage.getPixels(basePixels, 0, width, 0, 0, width, height);gainMap.getPixels(gainPixels, 0, width, 0, 0, width, height);// 直接复制基础图像到结果,避免暗像素的额外处理System.arraycopy(basePixels, 0, resultPixels, 0, basePixels.length);// 关键修复:analyzeBrightness 只调用一次!float[] brightnessStats = analyzeBrightness(basePixels, width, height);int brightCount = 0;// 处理每个像素for (int i = 0; i < basePixels.length; i++) {int basePixel = basePixels[i];// 快速亮度计算,避免函数调用开销int r = (basePixel >> 16) & 0xFF;int g = (basePixel >> 8) & 0xFF;int b = basePixel & 0xFF;// 快速亮度近似计算float pixelBrightness = (r * 0.299f + g * 0.587f + b * 0.114f) / 255.0f;// 提高亮度阈值,只处理真正的高光区域if (pixelBrightness < 0.85f) { // 从0.8提高到0.85continue;}brightCount++;int gainPixel = gainPixels[i];// 提取RGB分量float baseR = r / 255.0f;float baseG = g / 255.0f;float baseB = b / 255.0f;float gainR = ((gainPixel >> 16) & 0xFF) / 255.0f;float gainG = ((gainPixel >> 8) & 0xFF) / 255.0f;float gainB = (gainPixel & 0xFF) / 255.0f;// 应用自适应GainMap算法float[] hdrColor = applyAdaptiveGainMap(baseR, baseG, baseB,gainR, gainG, gainB,pixelBrightness, brightnessStats,gamma, hdrCapacityMin, hdrCapacityMax,offsetSdr, offsetHdr);// 转换回ARGB并钳制int resultR = Math.max(0, Math.min(255, (int)(hdrColor[0] * 255)));int resultG = Math.max(0, Math.min(255, (int)(hdrColor[1] * 255)));int resultB = Math.max(0, Math.min(255, (int)(hdrColor[2] * 255)));resultPixels[i] = (basePixel & 0xFF000000) | (resultR << 16) | (resultG << 8) | resultB;}LogUtils.Companion.d("BBB", "brightCount = " + brightCount);result.setPixels(resultPixels, 0, width, 0, 0, width, height);}/*** 分析图像亮度统计信息*/private static float[] analyzeBrightness(int[] pixels, int width, int height) {float totalBrightness = 0;float minBrightness = 1.0f;float maxBrightness = 0.0f;int sampleCount = 0;// 采样分析亮度for (int i = 0; i < pixels.length; i += 4) {int pixel = pixels[i];float r = ((pixel >> 16) & 0xFF) / 255.0f;float g = ((pixel >> 8) & 0xFF) / 255.0f;float b = (pixel & 0xFF) / 255.0f;float brightness = calculateLuminance(r, g, b);totalBrightness += brightness;minBrightness = Math.min(minBrightness, brightness);maxBrightness = Math.max(maxBrightness, brightness);sampleCount++;}float avgBrightness = totalBrightness / sampleCount;return new float[] {avgBrightness,    // 平均亮度minBrightness,    // 最小亮度maxBrightness,    // 最大亮度maxBrightness - minBrightness // 亮度范围};}/*** 自适应GainMap应用算法 - 优化曝光*/private static float[] applyAdaptiveGainMap(float baseR, float baseG, float baseB,float gainR, float gainG, float gainB,float pixelBrightness, float[] brightnessStats,float gamma, float hdrCapacityMin,float hdrCapacityMax, float offsetSdr,float offsetHdr) {// 1. 对基础图像进行伽马解码float[] linearBase = srgbToLinear(baseR, baseG, baseB);// 2. 计算基础增益值 - 使用更保守的增益计算float baseGain = calculateConservativeGainFromGainMap(gainR, gainG, gainB, gamma,hdrCapacityMin, hdrCapacityMax);// 3. 根据像素亮度自适应调整增益 - 使用更保守的策略float adaptiveGain = calculateConservativeAdaptiveGain(baseGain, pixelBrightness, brightnessStats);// 4. 应用增益 - 使用更保守的增益应用float[] hdrLinear = applyConservativeGainToLinear(linearBase, adaptiveGain,pixelBrightness, offsetSdr, offsetHdr);// 5. 伽马编码回sRGB空间return linearToSrgb(hdrLinear[0], hdrLinear[1], hdrLinear[2]);}/*** 保守的增益计算 - 大幅降低增益强度*/private static float calculateConservativeGainFromGainMap(float gainR, float gainG, float gainB,float gamma, float hdrCapacityMin,float hdrCapacityMax) {// 使用增益图的亮度信息float gainLuminance = calculateLuminance(gainR, gainG, gainB);// 对增益图进行伽马解码float decodedGain = (float) Math.pow(gainLuminance, 1.0 / gamma);// 归一化处理 - 使用更窄的范围float normalizedGain = (decodedGain - hdrCapacityMin) / (hdrCapacityMax - hdrCapacityMin);normalizedGain = Math.max(0.1f, Math.min(0.8f, normalizedGain)); // 上限从1.0降到0.8// 转换为线性增益值 - 大幅降低增益范围return 1.0f + normalizedGain * 0.6f; // 增益范围:1.0x - 1.6x (原来是1.0x - 2.5x)}/*** 保守的自适应增益调整*/private static float calculateConservativeAdaptiveGain(float baseGain, float pixelBrightness,float[] brightnessStats) {float avgBrightness = brightnessStats[0];float minBrightness = brightnessStats[1];float maxBrightness = brightnessStats[2];float brightnessRange = brightnessStats[3];// 更保守的自适应因子float adaptiveFactor;if (pixelBrightness < 0.3f) {// 暗部区域:轻微增加增益adaptiveFactor = 1.0f + (0.3f - pixelBrightness) * 0.3f; // 从1.5降到0.3} else if (pixelBrightness > 0.7f) {// 高光区域:显著降低增益adaptiveFactor = 0.7f + (1.0f - pixelBrightness) * 0.1f; // 从0.2降到0.1} else {// 中间调:基本不调整adaptiveFactor = 1.0f;}// 根据图像整体亮度进一步调整 - 更保守float imageBrightnessFactor;if (avgBrightness < 0.3f) {// 整体偏暗的图像:轻微增加整体增益imageBrightnessFactor = 1.0f + (0.3f - avgBrightness) * 0.3f; // 从1.0降到0.3} else if (avgBrightness > 0.7f) {// 整体偏亮的图像:降低整体增益imageBrightnessFactor = 0.8f; // 从0.7提高到0.8} else {imageBrightnessFactor = 1.0f;}return baseGain * adaptiveFactor * imageBrightnessFactor;}/*** 保守的增益应用 - 防止过曝*/private static float[] applyConservativeGainToLinear(float[] linearBase, float gain,float pixelBrightness,float offsetSdr, float offsetHdr) {float[] result = new float[3];for (int i = 0; i < 3; i++) {float baseChannel = linearBase[i];// 更小的偏移量float dynamicOffsetSdr = offsetSdr * (1.0f - pixelBrightness) * 0.05f; // 从0.1降到0.05float dynamicOffsetHdr = offsetHdr * (1.0f - pixelBrightness) * 0.05f; // 从0.1降到0.05// 应用偏移float adjustedBase = baseChannel + dynamicOffsetSdr;// 应用增益float hdrChannel = adjustedBase * gain;// 应用HDR偏移hdrChannel += dynamicOffsetHdr;// 更严格的动态钳制float maxValue;if (pixelBrightness > 0.9f) {maxValue = 1.0f; // 极高光区域严格限制} else if (pixelBrightness > 0.8f) {maxValue = 1.2f; // 高光区域较严格限制} else {maxValue = 1.5f; // 其他区域适中限制}hdrChannel = Math.max(0, Math.min(maxValue, hdrChannel));result[i] = hdrChannel;}return result;}/*** sRGB到线性颜色空间转换*/private static float[] srgbToLinear(float r, float g, float b) {return new float[] {srgbToLinearSingle(r),srgbToLinearSingle(g),srgbToLinearSingle(b)};}private static float srgbToLinearSingle(float channel) {if (channel <= 0.04045f) {return channel / 12.92f;} else {return (float) Math.pow((channel + 0.055f) / 1.055f, 2.4f);}}/*** 计算亮度*/private static float calculateLuminance(float r, float g, float b) {return 0.2126f * r + 0.7152f * g + 0.0722f * b;}/*** 线性到sRGB颜色空间转换*/private static float[] linearToSrgb(float r, float g, float b) {return new float[] {linearToSrgbSingle(r),linearToSrgbSingle(g),linearToSrgbSingle(b)};}private static float linearToSrgbSingle(float channel) {if (channel <= 0.0031308f) {return channel * 12.92f;} else {return (float) (1.055f * Math.pow(channel, 1.0 / 2.4) - 0.055f);}}/*** 超保守版本 - 防止过曝*/public static Bitmap applyGainMapConservative(Bitmap baseImage, Bitmap gainMap) {// 使用更保守的参数float gamma = 1.8f; // 更高的gamma值float hdrCapacityMin = 0.3f; // 提高最小值float hdrCapacityMax = 0.6f; // 降低最大值float offsetSdr = 0.005f; // 减小偏移float offsetHdr = 0.002f; // 减小偏移return applyGainMapAlgorithm(baseImage, gainMap,gamma, hdrCapacityMin, hdrCapacityMax, offsetSdr, offsetHdr);}/*** 轻度增强版本 - 平衡效果和自然度*/public static Bitmap applyGainMapBalanced(Bitmap baseImage, Bitmap gainMap) {float gamma = 1.5f;float hdrCapacityMin = 0.25f;float hdrCapacityMax = 0.65f;float offsetSdr = 0.008f;float offsetHdr = 0.004f;return applyGainMapAlgorithm(baseImage, gainMap,gamma, hdrCapacityMin, hdrCapacityMax, offsetSdr, offsetHdr);}
}

hdr_image_decoder_main.xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"xmlns:tools="http://schemas.android.com/tools"xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><androidx.appcompat.widget.AppCompatButtonandroid:id="@+id/hdr_show_btn1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:textAllCaps="false"android:textSize="22sp"android:text="图片1"/><androidx.appcompat.widget.AppCompatButtonandroid:id="@+id/hdr_show_btn2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:layout_gravity="center_horizontal"android:textAllCaps="false"android:textSize="22sp"android:text="图片2 - GainMap算法"/></LinearLayout><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/hdr_show_result"android:layout_width="match_parent"android:scaleType="centerCrop"android:layout_height="260dp"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="18dp"android:layout_marginLeft="20dp"android:textSize="28sp"android:textColor="@color/black"android:text="GainMap图"/><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/hdr_show_result2"android:layout_width="match_parent"android:scaleType="centerCrop"android:layout_height="260dp"/></LinearLayout>

http://www.dtcms.com/a/560934.html

相关文章:

  • 查找及其算法
  • Java 高级特性:泛型与包装类深度解析
  • GD32F407VE天空星开发板的旋转编码器EC12的实现
  • 从零开始学习Redis(五):多级缓存
  • 解码LVGL样式
  • 山西响应式网站建设价位企业培训计划
  • 深入浅出 C++ 多态:从概念到原理
  • 多实现类(如IService有ServiceA/ServiceB)的注入配置与获取
  • web自动化测试-Selenium04_iframe切换、窗口切换
  • 分类与回归算法(一)- 模型评价指标
  • 浙江十大建筑公司排名用v9做网站优化
  • 江门网站建设自助建站站内seo和站外seo区别
  • 嵌入式Linux:线程同步(自旋锁)
  • RHCE复习第一次作业
  • 2025年山西省职业院校技能大赛应用软件系统开发赛项竞赛样题
  • 铁路机车乘务员心理健康状况的研究进展
  • 人才市场官方网站装修公司网站平台
  • Flink 2.1 SQL:解锁实时数据与AI集成,实现可扩展流处理
  • 【软件安全】什么是AFL(American Fuzzy Lop)基于覆盖率引导的模糊测试工具?
  • 山西省最新干部调整佛山网站建设优化
  • 背包DP合集
  • Docker 拉取镜像:SSL 拦截与国内镜像源失效问题解决
  • full join优化改写经验
  • 软件测试:黑盒测试用例篇
  • 【Linux】Linux第一个小程序 - 进度条
  • ubuntu新增用户
  • 青州市网站建设长沙招聘网58同城招聘发布
  • 江苏中南建设集团网站是多少长沙互联网网站建设
  • 从零开始的云原生之旅(十一):压测实战:验证弹性伸缩效果
  • 民宿网站的建设wordpress gallery