diff --git a/Android Termux stuff b/Android Termux stuff
new file mode 100644
index 000000000..97e28a635
--- /dev/null
+++ b/Android Termux stuff
@@ -0,0 +1,87 @@
+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
+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"
+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 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"
+
+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"
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..0de68e599 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,188 @@
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 String no_ois;
+
+ private static int timelapse_interval;
+
+ static private String filePath;
+ static String photoFilePath;
+
+ static String cameraId;
+
+ static int photo_number;
+
+ static int photos_to_take;
+
+ static int focus_start;
+ static int focus_end;
+ static int focus_steps;
+ static int cur_focus_step;
+
+ static int res_x;
+ static int res_y;
+
+ 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 +239,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;
}
- takePicture(stdout, context, new File(photoFilePath), cameraId);
+ 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;
+ }
+
+ 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 +272,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 +316,18 @@ 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;
- }
+ Rect sensor_size;
+ sensor_size = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+
+ if(res_x==0 && res_y==0)
+ {
+ res_x=sensor_size.width();
+ res_y=sensor_size.height();
}
- 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);
+
+ 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() {
@@ -149,7 +338,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 +360,104 @@ 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);
+ }
+
+ //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");
+ 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);
+ }
+
+ //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
+ 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);
@@ -266,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));
@@ -285,7 +554,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'
}
}
diff --git a/timelapse_focus_stack.sh b/timelapse_focus_stack.sh
new file mode 100644
index 000000000..529538f0f
--- /dev/null
+++ b/timelapse_focus_stack.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