From a889b2b73bcaef004cb3b79aca29e529f19172d7 Mon Sep 17 00:00:00 2001 From: raduprv Date: Thu, 18 Jul 2024 01:44:48 +0300 Subject: [PATCH 01/11] Update CameraPhotoAPI.java Added a few new features: Fixed the sensor max resolution, before it was limited to 12MP, at least on my Xiaomi Mi Note 10. Added support for setting the iso/exposure time, EV compensation, flash control (the auto mode doesn't work on my phone though), and focus distance. --- .../com/termux/api/apis/CameraPhotoAPI.java | 162 ++++++++++++++---- 1 file changed, 129 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java index 3cc6738a0..1b5e76138 100644 --- a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java +++ b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java @@ -1,8 +1,16 @@ package com.termux.api.apis; +import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_OFF; +import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON; +import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH; +import static android.hardware.camera2.CameraMetadata.FLASH_MODE_OFF; +import static android.hardware.camera2.CameraMetadata.FLASH_MODE_SINGLE; +import static java.lang.Float.parseFloat; + import android.content.Context; import android.content.Intent; import android.graphics.ImageFormat; +import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; @@ -15,6 +23,7 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; +import android.os.Build; import android.os.Looper; import android.util.Size; import android.view.Surface; @@ -31,29 +40,60 @@ import java.io.FileOutputStream; import java.io.PrintWriter; import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Objects; public class CameraPhotoAPI { private static final String LOG_TAG = "CameraPhotoAPI"; + private static float focus_distance=0; + private static Integer iso=0; + private static Integer exposure=0; + + private static Integer ev_steps=0; + + private static String flash; + + private static String no_processing; + public static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) { Logger.logDebug(LOG_TAG, "onReceive"); - final String filePath = intent.getStringExtra("file"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US); + //String filePath = intent.getStringExtra("file"); + final String filePath = Objects.toString(intent.getStringExtra("file"), "/data/data/com.termux/files/home/Photos/" + sdf.format(new Date()) + ".jpg"); final String cameraId = Objects.toString(intent.getStringExtra("camera"), "0"); + flash = Objects.toString(intent.getStringExtra("flash"), "off"); + no_processing = Objects.toString(intent.getStringExtra("no_processing"), "off"); + iso=Integer.parseInt(Objects.toString(intent.getStringExtra("iso"), "0")); + exposure=Integer.parseInt(Objects.toString(intent.getStringExtra("exposure"), "0")); + ev_steps=Integer.parseInt(Objects.toString(intent.getStringExtra("ev_steps"), "0")); + focus_distance=parseFloat(Objects.toString(intent.getStringExtra("focus"), "0")); ResultReturner.returnData(apiReceiver, intent, stdout -> { if (filePath == null || filePath.isEmpty()) { + stdout.println("ERROR: " + "File path not passed"); return; } + + stdout.println("ISO: " + iso); + stdout.println("Exposure: " + exposure); + stdout.println("Focus: " + focus_distance); + stdout.println("EV steps: " + ev_steps); + stdout.println("File path: " + filePath); + stdout.println("Flash: " + flash); + + // Get canonical path of photoFilePath String photoFilePath = TermuxFileUtils.getCanonicalPath(filePath, null, true); String photoDirPath = FileUtils.getFileDirname(photoFilePath); @@ -69,6 +109,7 @@ public static void onReceive(TermuxApiReceiver apiReceiver, final Context contex false, true); if (error != null) { stdout.println("ERROR: " + error.getErrorLogString()); + stdout.println("Photo dir path: " + photoDirPath); return; } @@ -121,24 +162,13 @@ static void proceedWithOpenedCamera(final Context context, final CameraManager m final CameraCharacteristics characteristics = manager.getCameraCharacteristics(camera.getId()); - int autoExposureMode = CameraMetadata.CONTROL_AE_MODE_OFF; - for (int supportedMode : characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)) { - if (supportedMode == CameraMetadata.CONTROL_AE_MODE_ON) { - autoExposureMode = supportedMode; - } - } - final int autoExposureModeFinal = autoExposureMode; - - // Use largest available size: - StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - Comparator bySize = (lhs, rhs) -> { - // Cast to ensure multiplications won't overflow: - return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); - }; - List sizes = Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)); - Size largest = Collections.max(sizes, bySize); - - final ImageReader mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2); + //final int autoExposureModeFinal = CameraMetadata.CONTROL_AE_MODE_ON; + + Rect sensor_size; + sensor_size = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + stdout.println("Resolution is " + sensor_size.width() + "x" + sensor_size.height()); + + final ImageReader mImageReader = ImageReader.newInstance(sensor_size.width(), sensor_size.height(), ImageFormat.JPEG, 2); mImageReader.setOnImageAvailableListener(reader -> new Thread() { @Override public void run() { @@ -171,27 +201,93 @@ public void run() { @Override public void onConfigured(final CameraCaptureSession session) { try { - // create preview Request - CaptureRequest.Builder previewReq = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); - previewReq.addTarget(dummySurface); - previewReq.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); - previewReq.set(CaptureRequest.CONTROL_AE_MODE, autoExposureModeFinal); - - // continous preview-capture for 1/2 second - session.setRepeatingRequest(previewReq.build(), null, null); - Logger.logInfo(LOG_TAG, "preview started"); - Thread.sleep(500); - session.stopRepeating(); - Logger.logInfo(LOG_TAG, "preview stoppend"); + //no need for a preview if we don't need auto focus/exposure + //if(iso==0 || exposure==0 || focus_distance<0.01) + { + // create preview Request + CaptureRequest.Builder previewReq = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + previewReq.addTarget(dummySurface); + //previewReq.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + + if(focus_distance<0.01f) + previewReq.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + else + { + float focus_diopters = 1000.0f / focus_distance; + previewReq.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF); + previewReq.set(CaptureRequest.LENS_FOCUS_DISTANCE, focus_diopters); + } + + if(flash.equals("off")) + previewReq.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF); + else + if(flash.equals("on")) + previewReq.set(CaptureRequest.FLASH_MODE, FLASH_MODE_SINGLE); + + if(flash.equals("auto")) + previewReq.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON_AUTO_FLASH); + else + previewReq.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON); + + if(ev_steps!=0) + { + previewReq.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, ev_steps); + } + + // continous preview-capture for 1/2 second + session.setRepeatingRequest(previewReq.build(), null, null); + Logger.logInfo(LOG_TAG, "preview started"); + Thread.sleep(400); + session.stopRepeating(); + Logger.logInfo(LOG_TAG, "preview stoppend"); + } final CaptureRequest.Builder jpegRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // Render to our image reader: jpegRequest.addTarget(imageReaderSurface); // Configure auto-focus (AF) and auto-exposure (AE) modes: + + if(focus_distance<0.01f) jpegRequest.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); - jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, autoExposureModeFinal); + else + { + float focus_diopters = 1000.0f / focus_distance; + jpegRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF); + jpegRequest.set(CaptureRequest.LENS_FOCUS_DISTANCE, focus_diopters); + } + + if(flash.equals("off")) + jpegRequest.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF); + else + if(flash.equals("on")) + jpegRequest.set(CaptureRequest.FLASH_MODE, FLASH_MODE_SINGLE); + + if(iso!=0 && exposure!=0) + { + long forced_exposure=exposure*1000; + jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF); + jpegRequest.set(CaptureRequest.SENSOR_SENSITIVITY, iso); + jpegRequest.set(CaptureRequest.SENSOR_EXPOSURE_TIME, forced_exposure); + } + else + { + if(flash.equals("auto")) + jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON_AUTO_FLASH); + else + jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON); + } + jpegRequest.set(CaptureRequest.JPEG_ORIENTATION, correctOrientation(context, characteristics)); + if(no_processing.equals("on")) + { + jpegRequest.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF); + jpegRequest.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_OFF); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + jpegRequest.set(CaptureRequest.DISTORTION_CORRECTION_MODE, CaptureRequest.DISTORTION_CORRECTION_MODE_OFF); + } + } + saveImage(camera, session, jpegRequest.build()); } catch (Exception e) { Logger.logStackTraceWithMessage(LOG_TAG, "onConfigured() error in preview", e); @@ -288,4 +384,4 @@ static void closeCamera(CameraDevice camera, Looper looper) { if (looper != null) looper.quit(); } -} \ No newline at end of file +} From 3e7fd08aa7c3d851a2144891bc750ed785db8ccc Mon Sep 17 00:00:00 2001 From: raduprv Date: Fri, 19 Jul 2024 00:47:16 +0300 Subject: [PATCH 02/11] Update CameraPhotoAPI.java Added support for setting the preview time --- .../com/termux/api/apis/CameraPhotoAPI.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java index 1b5e76138..e0addccd9 100644 --- a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java +++ b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java @@ -20,12 +20,12 @@ import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.TotalCaptureResult; -import android.hardware.camera2.params.StreamConfigurationMap; + import android.media.Image; import android.media.ImageReader; import android.os.Build; import android.os.Looper; -import android.util.Size; + import android.view.Surface; import android.view.WindowManager; @@ -42,9 +42,7 @@ import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; + import java.util.Date; import java.util.List; import java.util.Locale; @@ -57,18 +55,18 @@ public class CameraPhotoAPI { private static float focus_distance=0; private static Integer iso=0; private static Integer exposure=0; - private static Integer ev_steps=0; - private static String flash; + private static Integer preview_time; + private static String no_processing; public static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) { Logger.logDebug(LOG_TAG, "onReceive"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US); - //String filePath = intent.getStringExtra("file"); + final String filePath = Objects.toString(intent.getStringExtra("file"), "/data/data/com.termux/files/home/Photos/" + sdf.format(new Date()) + ".jpg"); final String cameraId = Objects.toString(intent.getStringExtra("camera"), "0"); flash = Objects.toString(intent.getStringExtra("flash"), "off"); @@ -76,6 +74,8 @@ public static void onReceive(TermuxApiReceiver apiReceiver, final Context contex iso=Integer.parseInt(Objects.toString(intent.getStringExtra("iso"), "0")); exposure=Integer.parseInt(Objects.toString(intent.getStringExtra("exposure"), "0")); ev_steps=Integer.parseInt(Objects.toString(intent.getStringExtra("ev_steps"), "0")); + preview_time=Integer.parseInt(Objects.toString(intent.getStringExtra("preview_time"), "500")); + focus_distance=parseFloat(Objects.toString(intent.getStringExtra("focus"), "0")); ResultReturner.returnData(apiReceiver, intent, stdout -> { @@ -162,8 +162,6 @@ static void proceedWithOpenedCamera(final Context context, final CameraManager m final CameraCharacteristics characteristics = manager.getCameraCharacteristics(camera.getId()); - //final int autoExposureModeFinal = CameraMetadata.CONTROL_AE_MODE_ON; - Rect sensor_size; sensor_size = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); stdout.println("Resolution is " + sensor_size.width() + "x" + sensor_size.height()); @@ -202,7 +200,7 @@ public void run() { public void onConfigured(final CameraCaptureSession session) { try { //no need for a preview if we don't need auto focus/exposure - //if(iso==0 || exposure==0 || focus_distance<0.01) + if(preview_time>0) { // create preview Request CaptureRequest.Builder previewReq = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); @@ -237,7 +235,7 @@ public void onConfigured(final CameraCaptureSession session) { // continous preview-capture for 1/2 second session.setRepeatingRequest(previewReq.build(), null, null); Logger.logInfo(LOG_TAG, "preview started"); - Thread.sleep(400); + Thread.sleep(preview_time); session.stopRepeating(); Logger.logInfo(LOG_TAG, "preview stoppend"); } From 720517c2fd484e006357f76129abdd978d9fdccf Mon Sep 17 00:00:00 2001 From: Raduprv Date: Sun, 26 Jan 2025 01:48:29 +0200 Subject: [PATCH 03/11] Improved the camera, added an alarm api (in early stages) --- app/src/main/AndroidManifest.xml | 4 + .../com/termux/api/TermuxApiReceiver.java | 4 + .../java/com/termux/api/apis/AlarmAPI.java | 79 +++++ .../com/termux/api/apis/CameraPhotoAPI.java | 329 +++++++++++++++--- build.gradle | 2 +- 5 files changed, 375 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/com/termux/api/apis/AlarmAPI.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8411d0003..d78e4724e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -58,6 +58,7 @@ + + + + diff --git a/app/src/main/java/com/termux/api/TermuxApiReceiver.java b/app/src/main/java/com/termux/api/TermuxApiReceiver.java index 6efaafe16..e379bad11 100644 --- a/app/src/main/java/com/termux/api/TermuxApiReceiver.java +++ b/app/src/main/java/com/termux/api/TermuxApiReceiver.java @@ -8,6 +8,7 @@ import android.provider.Settings; import android.widget.Toast; +import com.termux.api.apis.AlarmAPI; import com.termux.api.apis.AudioAPI; import com.termux.api.apis.BatteryStatusAPI; import com.termux.api.apis.BrightnessAPI; @@ -87,6 +88,9 @@ private void doWork(Context context, Intent intent) { case "AudioInfo": AudioAPI.onReceive(this, context, intent); break; + case "Alarm": + AlarmAPI.onReceive(this, context, intent); + break; case "BatteryStatus": BatteryStatusAPI.onReceive(this, context, intent); break; diff --git a/app/src/main/java/com/termux/api/apis/AlarmAPI.java b/app/src/main/java/com/termux/api/apis/AlarmAPI.java new file mode 100644 index 000000000..320673fe2 --- /dev/null +++ b/app/src/main/java/com/termux/api/apis/AlarmAPI.java @@ -0,0 +1,79 @@ +package com.termux.api.apis; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.PowerManager; +import android.os.SystemClock; +import android.util.Log; +import android.widget.Toast; + +import com.termux.api.TermuxApiReceiver; +import com.termux.api.util.ResultReturner; +import com.termux.shared.logger.Logger; + +import java.io.IOException; +import java.util.Date; +import java.util.Objects; + +public class AlarmAPI { + + private static final String LOG_TAG = "AlarmAPI"; + private static TermuxApiReceiver current_receiver; + private static Intent original_intent; + private static int wl_duration; + + static public class AlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + + if(wl_duration!=0) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "termux_api:Wakelock"); + wl.acquire(wl_duration); + } + + final String command = Objects.toString(intent.getStringExtra("command")); + Log.i(LOG_TAG, "Command is: " + command); + + try { + + Process process = Runtime.getRuntime().exec(command); + } catch (IOException e) { + throw new RuntimeException(e); + } + //wl.release(); + } + + } + + public static void onReceive(TermuxApiReceiver apiReceiver, final Context context, Intent intent) { + Logger.logDebug(LOG_TAG, "onReceive"); + + current_receiver=apiReceiver; + original_intent=intent; + int seconds=Integer.parseInt(Objects.toString(intent.getStringExtra("seconds"), "0")); + wl_duration=Integer.parseInt(Objects.toString(intent.getStringExtra("wl_duration"), "0")); + final String command = Objects.toString(intent.getStringExtra("command")); + + Intent alarm_intent = new Intent(context, AlarmReceiver.class); + alarm_intent.putExtra("command", command); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarm_intent, 0); + + long nextTrigger = System.currentTimeMillis() +seconds*1000; + Log.i(LOG_TAG, System.currentTimeMillis() + ": scheduling next alarm at " + nextTrigger); + + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + + AlarmManager.AlarmClockInfo ac = new AlarmManager.AlarmClockInfo(nextTrigger, null); + alarmManager.setAlarmClock(ac, pendingIntent); + + + ResultReturner.noteDone(current_receiver, original_intent); + + } + + +} diff --git a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java index 3cc6738a0..c024eca7f 100644 --- a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java +++ b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java @@ -1,8 +1,19 @@ package com.termux.api.apis; +import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_OFF; +import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON; +import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH; +import static android.hardware.camera2.CameraMetadata.FLASH_MODE_OFF; +import static android.hardware.camera2.CameraMetadata.FLASH_MODE_SINGLE; +import static java.lang.Float.parseFloat; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.graphics.ImageFormat; +import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; @@ -12,11 +23,15 @@ import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.TotalCaptureResult; -import android.hardware.camera2.params.StreamConfigurationMap; + import android.media.Image; import android.media.ImageReader; +import android.os.Build; import android.os.Looper; -import android.util.Size; + +import android.os.PowerManager; +import android.os.SystemClock; +import android.util.Log; import android.view.Surface; import android.view.WindowManager; @@ -29,33 +44,179 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; + +import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Objects; +import java.util.concurrent.CountDownLatch; public class CameraPhotoAPI { private static final String LOG_TAG = "CameraPhotoAPI"; + private static AlarmManager alarmManager; + private static PendingIntent alarmIntent; + + private static float focus_distance=0; + private static Integer iso=0; + private static Integer exposure=0; + private static Integer ev_steps=0; + private static String flash; + + private static Integer preview_time; + + private static String no_processing; + + private static int timelapse_interval; + + static private String filePath; + static String photoFilePath; + + static String cameraId; + + static int photo_number=0; + + static int photos_to_take; + + static int focus_start; + static int focus_end; + static int focus_steps; + static int cur_focus_step; + + static CountDownLatch latch; + + + static public class AlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "termux_api:Wakelock"); + wl.acquire(); + + + new Thread(new Runnable() { + @Override + public void run() { + if(focus_steps>0) + { + do_focus_bracketing(null,context); + } + else + takePicture(null, context, new File(photoFilePath+String.format("%0" + 5 + "d", photo_number) + ".jpg"), cameraId); + + photos_to_take--; + photo_number++; + + // Reset the alarm for the next interval + if(photos_to_take>0) { + CameraPhotoAPI mainActivity = new CameraPhotoAPI(); + Logger.logInfo(LOG_TAG, "Resetting alarm!"); + mainActivity.setAlarm(timelapse_interval * 1000); + } + wl.release(); + } + }).start(); + + + + } + + } + + private static void initializeAlarmManager(Context context) { + alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(context, AlarmReceiver.class); + alarmIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } + + private static void setAlarm(long interval) { + alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + interval, alarmIntent); + } + + private static void do_focus_bracketing(final PrintWriter stdout,Context context) + { + int i; + float focus_increment=(focus_end-focus_start)/(float)focus_steps; + + focus_distance=focus_start; + + for(i=0;i { if (filePath == null || filePath.isEmpty()) { + stdout.println("ERROR: " + "File path not passed"); return; } + + stdout.println("ISO: " + iso); + stdout.println("Exposure: " + exposure); + stdout.println("Focus: " + focus_distance); + stdout.println("EV steps: " + ev_steps); + stdout.println("File path: " + filePath); + stdout.println("Flash: " + flash); + + // Get canonical path of photoFilePath - String photoFilePath = TermuxFileUtils.getCanonicalPath(filePath, null, true); + photoFilePath = TermuxFileUtils.getCanonicalPath(filePath, null, true); String photoDirPath = FileUtils.getFileDirname(photoFilePath); Logger.logVerbose(LOG_TAG, "photoFilePath=\"" + photoFilePath + "\", photoDirPath=\"" + photoDirPath + "\""); @@ -69,10 +230,32 @@ public static void onReceive(TermuxApiReceiver apiReceiver, final Context contex false, true); if (error != null) { stdout.println("ERROR: " + error.getErrorLogString()); + stdout.println("Photo dir path: " + photoDirPath); + return; + } + + if(focus_steps>0 && timelapse_interval==0) + { + if(focus_start==0 || focus_end==0) + stdout.println("Can't do focus bracketing without specifying the start and end points"); + else do_focus_bracketing(stdout,context); return; } - takePicture(stdout, context, new File(photoFilePath), cameraId); + if(timelapse_interval!=0) { + initializeAlarmManager(context); + CameraPhotoAPI.setAlarm(timelapse_interval * 1000); + if(focus_steps>0) + do_focus_bracketing(stdout,context); + else + takePicture(stdout, context, new File(photoFilePath+String.format("%0" + 5 + "d", photo_number) + ".jpg"), cameraId); + + photos_to_take--; + photo_number++; + } + else + takePicture(stdout, context, new File(photoFilePath+ ".jpg"), cameraId); + }); } @@ -80,7 +263,10 @@ private static void takePicture(final PrintWriter stdout, final Context context, try { final CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); - Looper.prepare(); + + if (Looper.myLooper() == null) + Looper.prepare(); + final Looper looper = Looper.myLooper(); //noinspection MissingPermission @@ -121,24 +307,11 @@ static void proceedWithOpenedCamera(final Context context, final CameraManager m final CameraCharacteristics characteristics = manager.getCameraCharacteristics(camera.getId()); - int autoExposureMode = CameraMetadata.CONTROL_AE_MODE_OFF; - for (int supportedMode : characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)) { - if (supportedMode == CameraMetadata.CONTROL_AE_MODE_ON) { - autoExposureMode = supportedMode; - } - } - final int autoExposureModeFinal = autoExposureMode; - - // Use largest available size: - StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - Comparator bySize = (lhs, rhs) -> { - // Cast to ensure multiplications won't overflow: - return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); - }; - List sizes = Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)); - Size largest = Collections.max(sizes, bySize); - - final ImageReader mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2); + Rect sensor_size; + sensor_size = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + if(stdout!=null)stdout.println("Resolution is " + sensor_size.width() + "x" + sensor_size.height()); + + final ImageReader mImageReader = ImageReader.newInstance(sensor_size.width(), sensor_size.height(), ImageFormat.JPEG, 2); mImageReader.setOnImageAvailableListener(reader -> new Thread() { @Override public void run() { @@ -149,7 +322,7 @@ public void run() { try (FileOutputStream output = new FileOutputStream(outputFile)) { output.write(bytes); } catch (Exception e) { - stdout.println("Error writing image: " + e.getMessage()); + if(stdout!=null)stdout.println("Error writing image: " + e.getMessage()); Logger.logStackTraceWithMessage(LOG_TAG, "Error writing image", e); } } finally { @@ -171,27 +344,96 @@ public void run() { @Override public void onConfigured(final CameraCaptureSession session) { try { - // create preview Request - CaptureRequest.Builder previewReq = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); - previewReq.addTarget(dummySurface); - previewReq.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); - previewReq.set(CaptureRequest.CONTROL_AE_MODE, autoExposureModeFinal); - - // continous preview-capture for 1/2 second - session.setRepeatingRequest(previewReq.build(), null, null); - Logger.logInfo(LOG_TAG, "preview started"); - Thread.sleep(500); - session.stopRepeating(); - Logger.logInfo(LOG_TAG, "preview stoppend"); + //no need for a preview if we don't need auto focus/exposure + if(preview_time>0) + { + // create preview Request + CaptureRequest.Builder previewReq = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + previewReq.addTarget(dummySurface); + //previewReq.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + + if(focus_distance<0.01f) + previewReq.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + else + { + float focus_diopters = 1000.0f / focus_distance; + previewReq.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF); + previewReq.set(CaptureRequest.LENS_FOCUS_DISTANCE, focus_diopters); + } + + if(flash.equals("off")) + previewReq.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF); + else + if(flash.equals("on")) + previewReq.set(CaptureRequest.FLASH_MODE, FLASH_MODE_SINGLE); + + if(flash.equals("auto")) + previewReq.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON_AUTO_FLASH); + else + previewReq.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON); + + if(ev_steps!=0) + { + previewReq.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, ev_steps); + } + + // continous preview-capture for 1/2 second + session.setRepeatingRequest(previewReq.build(), null, null); + Logger.logInfo(LOG_TAG, "preview started"); + Thread.sleep(preview_time); + session.stopRepeating(); + Logger.logInfo(LOG_TAG, "preview stoppend"); + } final CaptureRequest.Builder jpegRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // Render to our image reader: jpegRequest.addTarget(imageReaderSurface); // Configure auto-focus (AF) and auto-exposure (AE) modes: + + if(focus_distance<0.01f) jpegRequest.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); - jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, autoExposureModeFinal); + else + { + float focus_diopters = 1000.0f / focus_distance; + jpegRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF); + jpegRequest.set(CaptureRequest.LENS_FOCUS_DISTANCE, focus_diopters); + } + + if(flash.equals("off")) + jpegRequest.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF); + else + if(flash.equals("on")) + jpegRequest.set(CaptureRequest.FLASH_MODE, FLASH_MODE_SINGLE); + + if(iso!=0 && exposure!=0) + { + long forced_exposure=exposure*1000; + jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF); + jpegRequest.set(CaptureRequest.SENSOR_SENSITIVITY, iso); + jpegRequest.set(CaptureRequest.SENSOR_EXPOSURE_TIME, forced_exposure); + } + else + { + if(flash.equals("auto")) + jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON_AUTO_FLASH); + else + jpegRequest.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_ON); + } + jpegRequest.set(CaptureRequest.JPEG_ORIENTATION, correctOrientation(context, characteristics)); + if(no_processing.equals("on")) + { + jpegRequest.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF); + jpegRequest.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_OFF); + /* + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + jpegRequest.set(CaptureRequest.DISTORTION_CORRECTION_MODE, CaptureRequest.DISTORTION_CORRECTION_MODE_OFF); + } + */ + + } + saveImage(camera, session, jpegRequest.build()); } catch (Exception e) { Logger.logStackTraceWithMessage(LOG_TAG, "onConfigured() error in preview", e); @@ -285,7 +527,10 @@ static void closeCamera(CameraDevice camera, Looper looper) { } catch (RuntimeException e) { Logger.logInfo(LOG_TAG, "Exception closing camera: " + e.getMessage()); } + //latch.countDown(); + Logger.logInfo(LOG_TAG, "Latch released by close camera!"); if (looper != null) looper.quit(); + if(latch!=null)latch.countDown(); } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index dc71be303..f97df04b1 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:7.3.1" + classpath 'com.android.tools.build:gradle:7.4.2' } } From 7afa74770eacf647bb7e77077aff3f26c8073855 Mon Sep 17 00:00:00 2001 From: raduprv Date: Sun, 26 Jan 2025 22:27:24 +0200 Subject: [PATCH 04/11] Create Android Termux notes --- Android Termux stuff | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Android Termux stuff diff --git a/Android Termux stuff b/Android Termux stuff new file mode 100644 index 000000000..a9f70e3f1 --- /dev/null +++ b/Android Termux stuff @@ -0,0 +1,12 @@ +Focus stacking: + +pkg install git +pkg install make +pkg install clang +apt install x11-repo && apt update && apt install opencv +pkg install pkg-config +git clone https://github.com/PetteriAimonen/focus-stack.git +cd focus-stack/ +make +cd build +./focus-stack ~/focus-stack From 1baa1c96fcd1ef359e4e4821f822a3948df07118 Mon Sep 17 00:00:00 2001 From: raduprv Date: Sun, 26 Jan 2025 22:58:35 +0200 Subject: [PATCH 05/11] Update Android Termux stuff --- Android Termux stuff | 1 + 1 file changed, 1 insertion(+) diff --git a/Android Termux stuff b/Android Termux stuff index a9f70e3f1..7092be618 100644 --- a/Android Termux stuff +++ b/Android Termux stuff @@ -10,3 +10,4 @@ cd focus-stack/ make cd build ./focus-stack ~/focus-stack +pkg install python (needed because for some reason OpenCV is linked with a python lib). From 12d28e35007d1bfc144d6644dbc2490c77616a96 Mon Sep 17 00:00:00 2001 From: raduprv Date: Mon, 27 Jan 2025 00:42:58 +0200 Subject: [PATCH 06/11] Update Android Termux stuff --- Android Termux stuff | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Android Termux stuff b/Android Termux stuff index 7092be618..6cc731f05 100644 --- a/Android Termux stuff +++ b/Android Termux stuff @@ -11,3 +11,33 @@ make cd build ./focus-stack ~/focus-stack pkg install python (needed because for some reason OpenCV is linked with a python lib). + +Disabling various Android things that kill your processes +(usb debug) +adb shell "/system/bin/device_config set_sync_disabled_for_tests persistent; /system/bin/device_config put activity_manager max_phantom_processes 2147483647; settings put global settings_enable_monitor_phantom_procs false" +adb shell "/system/bin/device_config put activity_manager power_check_max_cpu_1 256; /system/bin/device_config put activity_manager power_check_max_cpu_2 256; /system/bin/device_config put activity_manager power_check_max_cpu_3 256; /system/bin/device_config put activity_manager power_check_max_cpu_4 256;" +adb shell "settings put global activity_manager_constants power_check_max_cpu_1=256; settings put global activity_manager_constants power_check_max_cpu_2=256; settings put global activity_manager_constants power_check_max_cpu_3=256; settings put global activity_manager_constants power_check_max_cpu_4=256;" +(root) + +sudo device_config set_sync_disabled_for_tests persistent + +sudo device_config put activity_manager max_phantom_processes 2147483647 +sudo settings put global settings_enable_monitor_phantom_procs false + +(this prevents Android from killing your long running processes after a while. There is a "new" way and an "old" way to set those settings, but at least on some OSes, such as LineageOS22 the old way is used. So I am including both) +sudo settings put global activity_manager_constants power_check_max_cpu_1=256 +sudo settings put global activity_manager_constants power_check_max_cpu_2=256 +sudo settings put global activity_manager_constants power_check_max_cpu_3=256 +sudo settings put global activity_manager_constants power_check_max_cpu_4=256 +sudo device_config put activity_manager power_check_max_cpu_1 256 +sudo device_config put activity_manager power_check_max_cpu_2 256 +sudo device_config put activity_manager power_check_max_cpu_3 256 +sudo device_config put activity_manager power_check_max_cpu_4 256 + +Allowing your app to recive alarms more often (every minute) +(root) +sudo settings put global alarm_manager_constants min_interval 60000 +sudo device_config put alarm_manager min_interval 60000 +(adb shell) +adb shell "settings put global alarm_manager_constants min_interval=60000" +adb shell "/system/bin/device_config put alarm_manager_constants min_interval 60000" From 8311398402e83df453547f50ef5a4debf6b8b387 Mon Sep 17 00:00:00 2001 From: raduprv Date: Tue, 28 Jan 2025 01:44:29 +0200 Subject: [PATCH 07/11] Update Android Termux stuff --- Android Termux stuff | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Android Termux stuff b/Android Termux stuff index 6cc731f05..bc98ca4c4 100644 --- a/Android Termux stuff +++ b/Android Termux stuff @@ -15,7 +15,7 @@ pkg install python (needed because for some reason OpenCV is linked with a pytho Disabling various Android things that kill your processes (usb debug) adb shell "/system/bin/device_config set_sync_disabled_for_tests persistent; /system/bin/device_config put activity_manager max_phantom_processes 2147483647; settings put global settings_enable_monitor_phantom_procs false" -adb shell "/system/bin/device_config put activity_manager power_check_max_cpu_1 256; /system/bin/device_config put activity_manager power_check_max_cpu_2 256; /system/bin/device_config put activity_manager power_check_max_cpu_3 256; /system/bin/device_config put activity_manager power_check_max_cpu_4 256;" +adb shell "/system/bin/device_config put activity_manager power_check_max_cpu_1 256; /system/bin/device_config put activity_manager power_check_max_cpu_2 256; /system/bin/device_config put activity_manager power_check_max_cpu_3 256; /system/bin/device_config put activity_manager power_check_max_cpu_4 256;" adb shell "settings put global activity_manager_constants power_check_max_cpu_1=256; settings put global activity_manager_constants power_check_max_cpu_2=256; settings put global activity_manager_constants power_check_max_cpu_3=256; settings put global activity_manager_constants power_check_max_cpu_4=256;" (root) @@ -34,10 +34,27 @@ sudo device_config put activity_manager power_check_max_cpu_2 256 sudo device_config put activity_manager power_check_max_cpu_3 256 sudo device_config put activity_manager power_check_max_cpu_4 256 -Allowing your app to recive alarms more often (every minute) +Allowing your app to recive alarms more often when not idle (every minute) (root) sudo settings put global alarm_manager_constants min_interval 60000 sudo device_config put alarm_manager min_interval 60000 (adb shell) adb shell "settings put global alarm_manager_constants min_interval=60000" -adb shell "/system/bin/device_config put alarm_manager_constants min_interval 60000" +adb shell "/system/bin/device_config put alarm_manager_constants min_interval 60000" + +Allow background apps to run longer from broadcast receivers (such as all termux api stuff). It will show the App not responding menu, rather than kill it. Not ideal, but there is no other way of disabling this restriction without recompiling some Android source code: +(root) +sudo settings put secure anr_show_background 1 +(adb) +adb shell "settings put secure anr_show_background 1" + +Make the app receive more alarms than allowed while idle (once evey 9 minutes or so), and allowing it to do more work while it's idle. For some reason it doesn't work on LineageOS 22. +(root) +sudo settings put global alarm_manager_constants allow_while_idle_long_time=20000,allow_while_idle_whitelist_duration=300000 +sudo device_config put alarm_manager_constants allow_while_idle_long_time 20000 +sudo device_config put alarm_manager_constants allow_while_idle_whitelist_duration 300000 + +(adb) +adb shell "settings put global alarm_manager_constants allow_while_idle_long_time=20000,allow_while_idle_whitelist_duration=300000" +adb shell "/system/bin/device_config put alarm_manager_constants allow_while_idle_long_time 20000" +adb shell "/system/bin/device_config put alarm_manager_constants allow_while_idle_whitelist_duration 300000" From 4bb9617065a9ab633e41f5892286ba628e65e82e Mon Sep 17 00:00:00 2001 From: raduprv Date: Tue, 28 Jan 2025 03:00:08 +0200 Subject: [PATCH 08/11] Update Android Termux stuff --- Android Termux stuff | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Android Termux stuff b/Android Termux stuff index bc98ca4c4..97e28a635 100644 --- a/Android Termux stuff +++ b/Android Termux stuff @@ -12,6 +12,33 @@ cd build ./focus-stack ~/focus-stack pkg install python (needed because for some reason OpenCV is linked with a python lib). +Ultralytics +pkg update +pkg install make +pkg install clang +pkg install patchelf +pkg install ninja +pkg install cmake +pkg install pkg-config +pkg install python +apt install x11-repo && apt update && apt install opencv-python +pkg install python-torch +pkg i tur-repo +pkg i python-pandas +pip install ultralytics (it will fail at OpenCV) +pip install seaborn +pip install requests +pip install py-cpuinfo +pip install pyyaml +pkg install python-torchvision +pip install tqdm +pip install ultralytics-thop +pip install psutil +pkg install python-scipy +pip install ultralytics --no-deps +Once all is done, just type: "yolo" and see if it works. + + Disabling various Android things that kill your processes (usb debug) adb shell "/system/bin/device_config set_sync_disabled_for_tests persistent; /system/bin/device_config put activity_manager max_phantom_processes 2147483647; settings put global settings_enable_monitor_phantom_procs false" From 9dfe8b5a1fb95611ac03d1a9e713fc20c97e067e Mon Sep 17 00:00:00 2001 From: Raduprv Date: Thu, 30 Jan 2025 20:07:45 +0200 Subject: [PATCH 09/11] Improved the camera, added an alarm api (in early stages) --- .../com/termux/api/apis/CameraPhotoAPI.java | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java index c024eca7f..0de68e599 100644 --- a/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java +++ b/app/src/main/java/com/termux/api/apis/CameraPhotoAPI.java @@ -73,6 +73,8 @@ public class CameraPhotoAPI { private static String no_processing; + private static String no_ois; + private static int timelapse_interval; static private String filePath; @@ -80,7 +82,7 @@ public class CameraPhotoAPI { static String cameraId; - static int photo_number=0; + static int photo_number; static int photos_to_take; @@ -89,6 +91,9 @@ public class CameraPhotoAPI { static int focus_steps; static int cur_focus_step; + static int res_x; + static int res_y; + static CountDownLatch latch; @@ -187,6 +192,7 @@ public static void onReceive(TermuxApiReceiver apiReceiver, final Context contex cameraId = Objects.toString(intent.getStringExtra("camera"), "0"); flash = Objects.toString(intent.getStringExtra("flash"), "off"); no_processing = Objects.toString(intent.getStringExtra("no_processing"), "off"); + no_ois = Objects.toString(intent.getStringExtra("no_ois"), "off"); iso=Integer.parseInt(Objects.toString(intent.getStringExtra("iso"), "0")); exposure=Integer.parseInt(Objects.toString(intent.getStringExtra("exposure"), "0")); ev_steps=Integer.parseInt(Objects.toString(intent.getStringExtra("ev_steps"), "0")); @@ -196,6 +202,9 @@ public static void onReceive(TermuxApiReceiver apiReceiver, final Context contex focus_start =Integer.parseInt(Objects.toString(intent.getStringExtra("focus_start"), "0")); focus_end =Integer.parseInt(Objects.toString(intent.getStringExtra("focus_end"), "0")); focus_steps =Integer.parseInt(Objects.toString(intent.getStringExtra("focus_steps"), "0")); + res_x =Integer.parseInt(Objects.toString(intent.getStringExtra("res_x"), "0")); + res_y =Integer.parseInt(Objects.toString(intent.getStringExtra("res_y"), "0")); + photo_number =Integer.parseInt(Objects.toString(intent.getStringExtra("start_number"), "0")); focus_distance=parseFloat(Objects.toString(intent.getStringExtra("focus"), "0")); @@ -309,9 +318,16 @@ static void proceedWithOpenedCamera(final Context context, final CameraManager m Rect sensor_size; sensor_size = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); - if(stdout!=null)stdout.println("Resolution is " + sensor_size.width() + "x" + sensor_size.height()); - final ImageReader mImageReader = ImageReader.newInstance(sensor_size.width(), sensor_size.height(), ImageFormat.JPEG, 2); + if(res_x==0 && res_y==0) + { + res_x=sensor_size.width(); + res_y=sensor_size.height(); + } + + if(stdout!=null)stdout.println("Resolution is " + res_x + "x" + res_y); + + final ImageReader mImageReader = ImageReader.newInstance(res_x, res_y, ImageFormat.JPEG, 2); mImageReader.setOnImageAvailableListener(reader -> new Thread() { @Override public void run() { @@ -377,6 +393,10 @@ public void onConfigured(final CameraCaptureSession session) { previewReq.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, ev_steps); } + //useful when on a tripod + if(no_ois.equals("on")) + previewReq.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE_OFF); + // continous preview-capture for 1/2 second session.setRepeatingRequest(previewReq.build(), null, null); Logger.logInfo(LOG_TAG, "preview started"); @@ -399,6 +419,10 @@ public void onConfigured(final CameraCaptureSession session) { jpegRequest.set(CaptureRequest.LENS_FOCUS_DISTANCE, focus_diopters); } + //useful when on a tripod + if(no_ois.equals("on")) + jpegRequest.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE_OFF); + if(flash.equals("off")) jpegRequest.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF); else @@ -508,6 +532,9 @@ static int correctOrientation(final Context context, final CameraCharacteristics } else { jpegOrientation = sensorOrientation - deviceOrientation; } + + jpegOrientation=sensorOrientation; + // Add an extra 360 because (-90 % 360) == -90 and Android won't accept a negative rotation. jpegOrientation = (jpegOrientation + 360) % 360; Logger.logInfo(LOG_TAG, String.format("Returning JPEG orientation of %d degrees", jpegOrientation)); From a832eae2b0235622d1e27a2a96da054919962494 Mon Sep 17 00:00:00 2001 From: raduprv Date: Thu, 30 Jan 2025 20:26:04 +0200 Subject: [PATCH 10/11] Create timelapse_focus_stac.sh --- timelapse_focus_stac.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 timelapse_focus_stac.sh diff --git a/timelapse_focus_stac.sh b/timelapse_focus_stac.sh new file mode 100644 index 000000000..529538f0f --- /dev/null +++ b/timelapse_focus_stac.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Check if the user provided a path as an argument +if [ -z "$1" ]; then + echo "Usage: $0 /path/to/files/" + exit 1 +fi + +# Store the path provided as an argument +file_path="$1" + +# Loop through all files in the given path, sorted by the first sequence of zeros +for file in $(ls "${file_path}test_run"*_f*.jpg | sort -t_ -k2,2n); do + # Extract the run number (e.g., 00000, 00001) by removing the prefix and suffix + run_number=$(basename "$file" | sed 's/test_run\([0-9]\{5\}\)_f.*\.jpg/\1/') + + # Check if we've already processed this run_number + if [ "$run_number" != "$prev_run_number" ]; then + # If this is a new run_number, print the command that would be executed + #echo "./fs --output=file${run_number}.jpg --global-align --align-keep-size --nocrop ${file_path}test_run${run_number}_f*.jpg" + ./fs --align-keep-size --global-align --output=file${run_number}.jpg ${file_path}test_run${run_number}_f*.jpg + # Update the previous run_number to avoid reprocessing + prev_run_number="$run_number" + fi +done + +ffmpeg -framerate 24 -i file%05d.jpg -c:v libx264 -pix_fmt yuv420p -preset slow -crf 18 -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" output_video.mp4 From 204c38d514948104508fee0988239e5c8aa101e0 Mon Sep 17 00:00:00 2001 From: raduprv Date: Thu, 30 Jan 2025 20:26:27 +0200 Subject: [PATCH 11/11] Rename timelapse_focus_stac.sh to timelapse_focus_stack.sh --- timelapse_focus_stac.sh => timelapse_focus_stack.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename timelapse_focus_stac.sh => timelapse_focus_stack.sh (100%) diff --git a/timelapse_focus_stac.sh b/timelapse_focus_stack.sh similarity index 100% rename from timelapse_focus_stac.sh rename to timelapse_focus_stack.sh