调整实现

mtkhdr
Matthew 6 months ago
parent 4dd5bea17a
commit 31b8d04103

@ -8,7 +8,7 @@ android {
defaultConfig {
applicationId "com.xypower.mppreview"
minSdk 28
minSdk 30
targetSdk 30
versionCode 1
versionName "1.0"
@ -18,7 +18,7 @@ android {
externalNativeBuild {
cmake {
// cppFlags '-std=c++17 -frtti -fexceptions -Wno-error=format-security'
cppFlags '-std=c++17 -fexceptions -Wno-error=format-security -fopenmp'
cppFlags '-std=c++17 -fexceptions -Wno-error=format-security -fopenmp '
// cppFlags '-std=c++17 -Wno-error=format-security'
// arguments "-DANDROID_STL=c++_shared"
// arguments "-DNCNN_DISABLE_EXCEPTION=OFF", "-DOpenCV_DIR=" + opencvsdk + "/sdk/native/jni", "-DNCNN_ROOT=" + ncnnroot

@ -5,7 +5,8 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- A camera with RAW capability is required to use this application -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- A camera with RAW capability is required to use this application -->
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.raw" />
@ -18,7 +19,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MpPreview"
tools:targetApi="31">
android:requestLegacyExternalStorage="true"
tools:targetApi="30">
<activity
android:name=".CameraActivity"
android:exported="false"

@ -9,7 +9,7 @@ cmake_minimum_required(VERSION 3.22.1)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections -Wformat-security ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp -static-openmp -ffunction-sections -fdata-sections -Wformat-security ")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
# SET_TARGET_PROPERTIES(microphoto PROPERTIES LINK_FLAGS "-Wl,-s,--gc-sections")
@ -74,14 +74,16 @@ find_library( # Sets the name of the path variable.
target_link_libraries( # Specifies the target library.
${PROJECT_NAME}
PUBLIC -fopenmp -static-openmp
# Links the target library to the log library
# included in the NDK.
${log-lib}
android
android mediandk z jnigraphics
#ncnn
${OpenCV_LIBS}
)

@ -3,10 +3,150 @@
#include <vector>
// #include "ncnn/yolov5ncnn.h"
#include <fcntl.h>
#include <unistd.h>
#include <omp.h>
#include <android/imagedecoder.h>
#include <android/log.h>
#include <media/NdkImage.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#define HDR_TAG "HDR"
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, HDR_TAG,__VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, HDR_TAG,__VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, HDR_TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, HDR_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, HDR_TAG,__VA_ARGS__)
bool AndroidBitmap_CompressWriteFile(void *userContext, const void *data, size_t size)
{
int file = (int)((size_t)userContext);
int bytesWritten = write(file, data, size);
return bytesWritten == size;
}
bool AndroidBitmap_CompressWriteBuffer(void *userContext, const void *data, size_t size)
{
std::vector<uint8_t>* buffer = (std::vector<uint8_t>*)userContext;
// int bytesWritten = write(file, data, size);
const uint8_t* pBytes = (const uint8_t*)data;
buffer->insert(buffer->cend(), pBytes, pBytes + size);
return true;
}
void ConvertDngToPng(const uint8_t* buffer, size_t bufferLength, std::vector<uint8_t>& pngData)
{
AImageDecoder* imageDecoder = NULL;
AImageDecoder_createFromBuffer(buffer, bufferLength, &imageDecoder);
// int fd = open("/sdcard/com.xypower.mpapp/tmp/4.dng", O_RDONLY);
// AImageDecoder_createFromFd(fd, &imageDecoder);
const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(imageDecoder);
const char *mimeType = AImageDecoderHeaderInfo_getMimeType(headerInfo);
AndroidBitmapInfo bmpInfo = { 0 };
bmpInfo.flags = AImageDecoderHeaderInfo_getAlphaFlags(headerInfo);
bmpInfo.width = AImageDecoderHeaderInfo_getWidth(headerInfo);
bmpInfo.height = AImageDecoderHeaderInfo_getHeight(headerInfo);
bmpInfo.format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo);
bmpInfo.stride = AImageDecoder_getMinimumStride(imageDecoder); // Image decoder does not
// use padding by default
int32_t fmt = ANDROID_BITMAP_FORMAT_RGBA_8888;
size_t stride = bmpInfo.width * 4;
size_t size = stride * bmpInfo.height;
size = bmpInfo.stride * bmpInfo.height;
int32_t dataSpace = AImageDecoderHeaderInfo_getDataSpace(headerInfo);
std::vector<uint8_t> frame;
frame.resize(size);
// AImageDecoder_setTargetSize(imageDecoder, 5376, 3024);
int result = AImageDecoder_decodeImage(imageDecoder, (void *)(&frame[0]), bmpInfo.stride, size);
// close(fd);
if (result == ANDROID_IMAGE_DECODER_SUCCESS)
{
// std::string imagePath = "/data/data/com.xypower.mppreview/files/test.png";
// int file = open(imagePath.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
// if (file == -1) {}
pngData.clear();
AndroidBitmap_compress(&bmpInfo, dataSpace, &frame[0], ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100, (void*)&pngData, AndroidBitmap_CompressWriteBuffer);
// close(file);
std::vector<uint8_t> empty;
empty.swap(frame);
}
AImageDecoder_delete(imageDecoder);
}
void ConvertDngToPng(const uint8_t* buffer, size_t bufferLength, cv::Mat& rgb)
{
AImageDecoder* imageDecoder = NULL;
AImageDecoder_createFromBuffer(buffer, bufferLength, &imageDecoder);
// int fd = open("/sdcard/com.xypower.mpapp/tmp/4.dng", O_RDONLY);
// AImageDecoder_createFromFd(fd, &imageDecoder);
const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(imageDecoder);
const char *mimeType = AImageDecoderHeaderInfo_getMimeType(headerInfo);
AndroidBitmapInfo bmpInfo = { 0 };
bmpInfo.flags = AImageDecoderHeaderInfo_getAlphaFlags(headerInfo);
bmpInfo.width = AImageDecoderHeaderInfo_getWidth(headerInfo);
bmpInfo.height = AImageDecoderHeaderInfo_getHeight(headerInfo);
bmpInfo.format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo);
bmpInfo.stride = AImageDecoder_getMinimumStride(imageDecoder); // Image decoder does not
// use padding by default
int32_t fmt = ANDROID_BITMAP_FORMAT_RGBA_8888;
size_t stride = bmpInfo.width * 4;
size_t size = stride * bmpInfo.height;
size = bmpInfo.stride * bmpInfo.height;
int32_t dataSpace = AImageDecoderHeaderInfo_getDataSpace(headerInfo);
std::vector<uint8_t> frame;
frame.resize(size);
// AImageDecoder_setTargetSize(imageDecoder, 5376, 3024);
int result = AImageDecoder_decodeImage(imageDecoder, (void *)(&frame[0]), bmpInfo.stride, size);
// close(fd);
if (result == ANDROID_IMAGE_DECODER_SUCCESS)
{
cv::Mat tmp(bmpInfo.height, bmpInfo.width, CV_8UC4, &frame[0]);
tmp.copyTo(rgb);
//convert RGB to BGR
cv::cvtColor(rgb, rgb, cv::COLOR_RGB2BGR);
/*
// std::string imagePath = "/data/data/com.xypower.mppreview/files/test.png";
// int file = open(imagePath.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
// if (file == -1) {}
std::vector<uint8_t> pngData;
result = AndroidBitmap_compress(&bmpInfo, dataSpace, &frame[0], ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100, (void*)&pngData, AndroidBitmap_CompressWriteBuffer);
{
std::vector<uint8_t> empty;
empty.swap(frame);
}
// close(file);
if (ANDROID_BITMAP_RESULT_SUCCESS == result)
{
rgb = cv::imdecode(pngData, cv::IMREAD_COLOR);
}
*/
}
AImageDecoder_delete(imageDecoder);
}
inline std::string jstring2string(JNIEnv *env, jstring jStr)
{
@ -28,11 +168,12 @@ bool makeHdr(std::vector<float>& times, std::vector<std::string>& paths, cv::Mat
{
// Read images and exposure times
std::vector<cv::Mat> images;
images.resize(paths.size());
for (auto it = paths.cbegin(); it != paths.cend(); ++it)
#pragma omp parallel for
for (int idx = 0; idx < paths.size(); idx++)
{
cv::Mat im = cv::imread((*it).c_str());
images.push_back(im);
images[idx] = cv::imread(paths[idx].c_str());
}
// Align input images
// cout << "Aligning images ... " << endl;
@ -77,17 +218,66 @@ bool makeHdr(std::vector<float>& times, std::vector<std::string>& paths, cv::Mat
return true;
}
bool makeHdr(std::vector<float>& times, std::vector<cv::Mat>& images, cv::Mat& rgb)
{
// Read images and exposure times
// Align input images
// cout << "Aligning images ... " << endl;
cv::Ptr<cv::AlignMTB> alignMTB = cv::createAlignMTB();
#if 0
alignMTB->process(images, images);
#endif
// Obtain Camera Response Function (CRF)
// cout << "Calculating Camera Response Function (CRF) ... " << endl;
cv::Mat responseDebevec;
cv::Ptr<cv::CalibrateDebevec> calibrateDebevec = cv::createCalibrateDebevec();
calibrateDebevec->process(images, responseDebevec, times);
// Merge images into an HDR linear image
// cout << "Merging images into one HDR image ... ";
cv::Mat hdrDebevec;
cv::Ptr<cv::MergeDebevec> mergeDebevec = cv::createMergeDebevec();
mergeDebevec->process(images, hdrDebevec, times, responseDebevec);
// Save HDR image.
// imwrite((OUTPUT_DIR "hdrDebevec.hdr"), hdrDebevec);
// cout << "saved hdrDebevec.hdr " << endl;
{
std::vector<cv::Mat> empty;
empty.swap(images);
}
// Tonemap using Reinhard's method to obtain 24-bit color image
// cout << "Tonemaping using Reinhard's method ... ";
cv::Mat ldrReinhard;
cv::Ptr<cv::TonemapReinhard> tonemapReinhard = cv::createTonemapReinhard(1.5, 0, 0, 0);
tonemapReinhard->process(hdrDebevec, ldrReinhard);
hdrDebevec.release();
int type = ldrReinhard.type();
ldrReinhard = ldrReinhard * 255;
ldrReinhard.convertTo(rgb, CV_8U);
ldrReinhard.release();
return true;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_xypower_mppreview_Camera2RawFragment_makeHdr(
JNIEnv *env, jobject thiz, jlong exposureTime1, jstring path1, jlong exposureTime2,
jstring path2, jstring outputPath) {
cv::setNumThreads(4);
std::vector<float> times;
std::vector<std::string> paths;
times.push_back((float)(exposureTime1 / 1000) / 1000000.0);
times.push_back((float)(exposureTime2 / 1000) / 1000000.0);
times.push_back((double)(exposureTime1) / 1000000000.0);
times.push_back((double)(exposureTime2) / 1000000000.0);
paths.push_back(jstring2string(env, path1));
paths.push_back(jstring2string(env, path2));
@ -111,6 +301,7 @@ Java_com_xypower_mppreview_Camera2RawFragment_makeHdr2(
JNIEnv *env, jobject thiz, jlong exposureTime1, jstring path1, jlong exposureTime2,
jstring path2, jlong exposureTime3, jstring path3, jstring outputPath) {
cv::setNumThreads(4);
std::vector<float> times;
std::vector<std::string> paths;
@ -188,4 +379,58 @@ Java_com_xypower_mppreview_Camera2RawFragment_makeHdr2(
// /*Mat fusion;
// Ptr<MergeMertens> merge_mertens = createMergeMertens();
// merge_mertens->process(images, *(Mat *)hdrImg);*/
//}
//}
extern "C"
JNIEXPORT void JNICALL
Java_com_xypower_mppreview_MainActivity_test(JNIEnv *env, jobject thiz) {
// TODO: implement test()
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_xypower_mppreview_Camera2RawFragment_makeHdr3(JNIEnv *env, jclass clazz,
jlong exposureTime1, jobject img1, jint length1,
jlong exposureTime2, jobject img2, jint length2,
jstring outputPath) {
ALOGI("Start HDR3");
std::vector<cv::Mat> images;
images.resize(2);
std::vector<size_t> pngLengths;
pngLengths.push_back(length1);
pngLengths.push_back(length2);
std::vector<const uint8_t*> pngDatas;
pngDatas.resize(2);
pngDatas[0] = (const uint8_t*)env->GetDirectBufferAddress(img1);
pngDatas[1] = (const uint8_t*)env->GetDirectBufferAddress(img2);
// omp_set_num_threads(2);
#pragma omp parallel for num_threads(2)
for (int idx = 0; idx < 2; idx++)
{
ConvertDngToPng(pngDatas[idx], pngLengths[idx], images[idx]);
}
cv::Mat rgb;
std::vector<float> times;
times.push_back((double)(exposureTime1) / 1000000000.0);
times.push_back((double)(exposureTime2) / 1000000000.0);
ALOGI("Start MakeHDR3");
makeHdr(times, images, rgb);
ALOGI("End MakeHDR3");
std::string fileName = jstring2string(env, outputPath);
std::vector<int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(100);
if (cv::imwrite(fileName.c_str(), rgb, params))
{
ALOGI("End HDR3");
return JNI_TRUE;
}
return JNI_FALSE;
}

@ -0,0 +1,133 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xypower.mppreview;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* A ByteBuffer-backed OutputStream that expands the internal ByteBuffer as required. Given this, the caller should
* always access the underlying ByteBuffer via the {@link #buffer()} method until all writes are completed.
*
* This class is typically used for 2 purposes:
*
* 1. Write to a ByteBuffer when there is a chance that we may need to expand it in order to fit all the desired data
* 2. Write to a ByteBuffer via methods that expect an OutputStream interface
*
* Hard to track bugs can happen when this class is used for the second reason and unexpected buffer expansion happens.
* So, it's best to assume that buffer expansion can always happen. An improvement would be to create a separate class
* that throws an error if buffer expansion is required to avoid the issue altogether.
*/
public class ByteBufferOutputStream extends OutputStream {
private static final float REALLOCATION_FACTOR = 1.1f;
private final int initialCapacity;
private final int initialPosition;
private ByteBuffer buffer;
/**
* Creates an instance of this class that will write to the received `buffer` up to its `limit`. If necessary to
* satisfy `write` or `position` calls, larger buffers will be allocated so the {@link #buffer()} method may return
* a different buffer than the received `buffer` parameter.
*
* Prefer one of the constructors that allocate the internal buffer for clearer semantics.
*/
public ByteBufferOutputStream(ByteBuffer buffer) {
this.buffer = buffer;
this.initialPosition = buffer.position();
this.initialCapacity = buffer.capacity();
}
public ByteBufferOutputStream(int initialCapacity) {
this(initialCapacity, false);
}
public ByteBufferOutputStream(int initialCapacity, boolean directBuffer) {
this(directBuffer ? ByteBuffer.allocateDirect(initialCapacity) : ByteBuffer.allocate(initialCapacity));
}
public void write(int b) {
ensureRemaining(1);
buffer.put((byte) b);
}
public void write(byte[] bytes, int off, int len) {
ensureRemaining(len);
buffer.put(bytes, off, len);
}
public void write(ByteBuffer sourceBuffer) {
ensureRemaining(sourceBuffer.remaining());
buffer.put(sourceBuffer);
}
public ByteBuffer buffer() {
return buffer;
}
public int position() {
return buffer.position();
}
public int remaining() {
return buffer.remaining();
}
public int limit() {
return buffer.limit();
}
public void position(int position) {
ensureRemaining(position - buffer.position());
buffer.position(position);
}
/**
* The capacity of the first internal ByteBuffer used by this class. This is useful in cases where a pooled
* ByteBuffer was passed via the constructor and it needs to be returned to the pool.
*/
public int initialCapacity() {
return initialCapacity;
}
/**
* Ensure there is enough space to write some number of bytes, expanding the underlying buffer if necessary.
* This can be used to avoid incremental expansions through calls to {@link #write(int)} when you know how
* many total bytes are needed.
*
* @param remainingBytesRequired The number of bytes required
*/
public void ensureRemaining(int remainingBytesRequired) {
if (remainingBytesRequired > buffer.remaining())
expandBuffer(remainingBytesRequired);
}
private void expandBuffer(int remainingRequired) {
int expandSize = Math.max((int) (buffer.limit() * REALLOCATION_FACTOR), buffer.position() + remainingRequired);
ByteBuffer temp = ByteBuffer.allocate(expandSize);
int limit = limit();
buffer.flip();
temp.put(buffer);
buffer.limit(limit);
// reset the old buffer's position so that the partial data in the new buffer cannot be mistakenly consumed
// we should ideally only do this for the original buffer, but the additional complexity doesn't seem worth it
buffer.position(initialPosition);
buffer = temp;
}
}

@ -64,6 +64,7 @@ import com.xypower.mppreview.widget.ErrorDialog;
import java.io.File;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -138,6 +139,7 @@ public class Camera2RawFragment extends Fragment {
public static native boolean makeHdr2(long exposureTime1, String path1, long exposureTime2, String path2, long exposureTime3, String path3, String outputPath);
public static native boolean makeHdr3(long exposureTime1, ByteBuffer img1, int length1, long exposureTime2, ByteBuffer img2, int length2, String outputPath);
private int mExposureComp = MainActivity.ExposureComp;
@ -1102,6 +1104,31 @@ public class Camera2RawFragment extends Fragment {
ArrayList<PngPhotoBean> mlist = new ArrayList<>();
List<CaptureRequest> requests = new ArrayList<>();
double v = 0;
ImageSaver.ImagePair imagePair = new ImageSaver.ImagePair(2);
ImageSaver.ImagePairRunnable runnable = new ImageSaver.ImagePairRunnable(imagePair) {
@Override
public void run() {
final List<ImageSaver.ImageInfo> images = imagePair.getImages();
final String outputPath = "/sdcard/DCIM/";
new Thread(new Runnable() {
@Override
public void run() {
// makeHdr3()
if (images.size() != 2) {
return;
}
ImageSaver.ImageInfo img1 = images.get(0);
ImageSaver.ImageInfo img2 = images.get(1);
makeHdr3(img1.exposureTime, img1.byteBuffer, img1.length, img2.exposureTime, img2.byteBuffer, img2.length, outputPath + "HDR_" + HdrUtil.generateTimestamp() + ".jpg");
}
}).start();
}
};
imagePair.setRunnable(runnable);
for (int idx = 0; idx < 2; idx++) {
// Set request tag to easily track results in callbacks.
captureBuilder.setTag(mRequestCounter.getAndIncrement());
@ -1140,6 +1167,7 @@ public class Camera2RawFragment extends Fragment {
// ImageSaverBuilder jpegBuilder = new ImageSaverBuilder(activity).setCharacteristics(mCharacteristics);
ImageSaverBuilder rawBuilder = new ImageSaverBuilder(activity).setCharacteristics(mCharacteristics);//保存拍照参数
rawBuilder.setImagePair(imagePair);
rawBuilder.setCallback(new CompleteCallback() {
@Override
public void onResult() {

@ -9,7 +9,7 @@ import java.util.Locale;
public class HdrUtil {
public static String generateTimestamp() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.US);
return sdf.format(new Date());
}

@ -18,27 +18,88 @@ import android.widget.Toast;
import com.xypower.mppreview.bean.Contants;
import com.xypower.mppreview.bean.PngPhotoBean;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class ImageSaver implements Runnable {
private final static String TAG = "HDR";
private final Image mImage;
private final File mFile;
private final CaptureResult mCaptureResult;
private final CameraCharacteristics mCharacteristics;
private CompleteCallback mCallback;
private final Context mContext;
private final ImagePair mImagePair;
private final Camera2RawFragment.RefCountedAutoCloseable<ImageReader> mReader;
public static class ImageInfo {
public long exposureTime;
public int length;
public ByteBuffer byteBuffer;
ImageInfo(ByteBuffer bb, int length, long exp) {
this.exposureTime = exp;
this.length = length;
byteBuffer = bb;
}
}
public static class ImagePair {
private List<ImageInfo> mImages;
private int mExpectedCount;
private Runnable mRunnable;
public ImagePair(int expectedCount) {
mImages = new ArrayList<>();
mExpectedCount = expectedCount;
mRunnable = null;
}
public void setRunnable(Runnable runnable) {
mRunnable = runnable;
}
public void addImage(ByteBuffer byteBuffer, int length, long exp) {
boolean isFull = false;
ImageInfo imageInfo = new ImageInfo(byteBuffer, length, exp);
synchronized (mImages) {
mImages.add(imageInfo);
isFull = (mImages.size() == mExpectedCount);
}
if (mRunnable != null && isFull) {
mRunnable.run();
}
}
public List<ImageInfo> getImages() {
return mImages;
}
}
public static abstract class ImagePairRunnable implements Runnable {
protected ImagePair mImagePair;
public ImagePairRunnable(ImagePair imagePair) {
mImagePair = imagePair;
}
}
private ArrayList<PngPhotoBean> mlist = new ArrayList<>();//用来存储已拍照的照片名称
public ImageSaver(Image image, File file, CaptureResult result, CameraCharacteristics characteristics, Context context, Camera2RawFragment.RefCountedAutoCloseable<ImageReader> reader, ArrayList<PngPhotoBean> list,CompleteCallback callback) {
public ImageSaver(Image image, File file, CaptureResult result, CameraCharacteristics characteristics, Context context,
Camera2RawFragment.RefCountedAutoCloseable<ImageReader> reader, ArrayList<PngPhotoBean> list,
CompleteCallback callback, ImagePair imagePair) {
mImage = image;
mFile = file;
mCaptureResult = result;
@ -47,6 +108,7 @@ public class ImageSaver implements Runnable {
mReader = reader;
mlist = list;
mCallback = callback;
mImagePair = imagePair;
}
@Override
@ -73,16 +135,26 @@ public class ImageSaver implements Runnable {
}
case ImageFormat.RAW_SENSOR: {
DngCreator dngCreator = new DngCreator(mCharacteristics, mCaptureResult);
FileOutputStream output = null;
ByteBuffer byteBuffer = null;
ByteBufferOutputStream baos = null;
Long t = mCaptureResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
try {
output = new FileOutputStream(mFile);
dngCreator.writeImage(output, mImage);
byteBuffer = ByteBuffer.allocateDirect(mImage.getWidth() * mImage.getHeight() * 2 + 81768);
baos = new ByteBufferOutputStream(byteBuffer);
Log.d(TAG, "Before Saving DNG");
dngCreator.writeImage(baos, mImage);
Log.d(TAG, "After Saving DNG pos=" + byteBuffer.position());
mImagePair.addImage(byteBuffer, byteBuffer.position(), t.longValue());
success = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
mImage.close();
closeOutput(output);
closeOutput(baos);
}
break;
}
@ -91,49 +163,6 @@ public class ImageSaver implements Runnable {
}
}
mReader.close();
if (success) {
Long t = mCaptureResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
String directoryPath = directory.getPath();
File file = new File(directory, "create_" + t + "_" + generateTimestamp() + ".png");
String path = file.getPath();
try {
Log.e("测试测试", "开始转换");
RawToJpgConverter.convertRawToJpg(mFile.getPath(), path);
Log.e("测试测试", "转换结束");
} catch (Exception e) {
throw new RuntimeException(e);
}
PngPhotoBean bean = new PngPhotoBean();
bean.setEtime(t);
bean.setPath(path);
mlist.add(bean);
if (mFile.exists()) {
mFile.delete();
}
if (mlist.size() == 2) {
PngPhotoBean bean1 = mlist.get(0);
PngPhotoBean bean2 = mlist.get(1);
String path1 = bean1.getPath();
String path2 = bean2.getPath();
String outputPath = directoryPath + "/" + "hdr_" + generateTimestamp() + ".png";
Log.e("测试测试", "开始合成");
boolean b = makeHdr(bean1.getEtime(), path1, bean2.getEtime(), path2, outputPath);
Log.e("测试测试", "合成结束");
if (b) {
mCallback.onResult();
// Toast.makeText(mContext.getApplicationContext(), "图片HDR成功", Toast.LENGTH_SHORT).show();
File file1 = new File(path1);
File file2 = new File(path2);
if (file1.exists()) {
file1.delete();
}
if (file2.exists()) {
file2.delete();
}
}
}
}
}
private static void closeOutput(OutputStream outputStream) {

@ -19,6 +19,7 @@ public class ImageSaverBuilder {
public CameraCharacteristics mCharacteristics;
public Context mContext;
public Camera2RawFragment.RefCountedAutoCloseable<ImageReader> mReader;
public ImageSaver.ImagePair mImagePair;
private ArrayList<PngPhotoBean> mlist;
private CompleteCallback mCallback;
@ -46,6 +47,12 @@ public class ImageSaverBuilder {
return this;
}
public synchronized ImageSaverBuilder setImagePair(final ImageSaver.ImagePair imagePair) {
if (imagePair == null) throw new NullPointerException();
mImagePair = imagePair;
return this;
}
public synchronized ImageSaverBuilder setFile(final File file) {
if (file == null) throw new NullPointerException();
mFile = file;
@ -78,7 +85,7 @@ public class ImageSaverBuilder {
if (!isComplete()) {
return null;
}
return new ImageSaver(mImage, mFile, mCaptureResult, mCharacteristics, mContext, mReader, mlist,mCallback);
return new ImageSaver(mImage, mFile, mCaptureResult, mCharacteristics, mContext, mReader, mlist,mCallback, mImagePair);
}
public synchronized String getSaveLocation() {

@ -1,12 +1,17 @@
package com.xypower.mppreview;
import static java.lang.System.loadLibrary;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.Editable;
import android.view.View;
@ -17,8 +22,16 @@ import android.widget.Spinner;
import com.xypower.mppreview.bean.Contants;
import java.io.File;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemSelectedListener {
static {
loadLibrary("mppreview");
}
private static int MY_PERMISSIONS_REQUEST_FOREGROUND_SERVICE = 100;
public static int ExposureComp = 0;
private Button systakepic;
private Button hdrtakepic;
@ -26,12 +39,46 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
private int picsize = 0;
protected native void test();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initActivityResult();
String[] accessPermissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.FOREGROUND_SERVICE, Manifest.permission.READ_PHONE_STATE,
/*Manifest.permission.PACKAGE_USAGE_STATS,*/
/*Manifest.permission.SET_TIME,*/};
boolean needRequire = false;
for (String access : accessPermissions) {
int curPermission = ActivityCompat.checkSelfPermission(MainActivity.this, access);
if (curPermission != PackageManager.PERMISSION_GRANTED) {
needRequire = true;
break;
}
}
if (needRequire) {
ActivityCompat.requestPermissions(MainActivity.this, accessPermissions, MY_PERMISSIONS_REQUEST_FOREGROUND_SERVICE);
// return;
}
File file = this.getFilesDir();
String path = "/sdcard/com.xypower.mppreview/";
file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
new Thread(new Runnable() {
@Override
public void run() {
test();
}
}).start();
}
private void initView() {

Loading…
Cancel
Save