Merge branch 'main' of git.telavivmakers.space:tami/thermalcam_decoder
This commit is contained in:
commit
82d11e868f
122
cvview.py
122
cvview.py
|
@ -2,6 +2,7 @@ import os
|
|||
import cv2
|
||||
import argparse
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
# Set up the argument parser
|
||||
parser = argparse.ArgumentParser(description="Visualize image files and display pixel values on hover.")
|
||||
|
@ -28,51 +29,31 @@ def calibrate(x):
|
|||
#print('{}..{}'.format(ret.max(), ret.min()))
|
||||
return ret
|
||||
|
||||
|
||||
class state:
|
||||
calibrate = False
|
||||
|
||||
|
||||
# Function to display the image and pixel values along with the frame index
|
||||
def show_pixel_values(image_path):
|
||||
def mouse_event(event, x, y, flags, param):
|
||||
if event == cv2.EVENT_MOUSEMOVE:
|
||||
pixel_value = img[y, x]
|
||||
text = f'Value: {pixel_value}, Location: ({x},{y})'
|
||||
img_text = img.copy()
|
||||
# Overlay the frame index
|
||||
frame_index = get_frame_index(image_path)
|
||||
cv2.putText(img_text, f'Frame: {frame_index}', (10, img_text.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA)
|
||||
cv2.putText(img_text, text, (50, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA)
|
||||
cv2.imshow('Image', img_text)
|
||||
|
||||
img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
|
||||
if img is None:
|
||||
print(f"Failed to load image at {image_path}. Check the file path and integrity.")
|
||||
return False
|
||||
if state.calibrate:
|
||||
img = calibrate(img)
|
||||
cv2.namedWindow('Image')
|
||||
cv2.setMouseCallback('Image', mouse_event)
|
||||
cv2.imshow('Image', img)
|
||||
return True
|
||||
# Global variables for the last mouse position
|
||||
last_x, last_y = 0, 0
|
||||
img, calibrated_img = None, None
|
||||
|
||||
|
||||
# Function to get the frame index from the filename
|
||||
def get_frame_index(filename):
|
||||
return os.path.splitext(os.path.basename(filename))[0][-4:]
|
||||
return os.path.splitext(os.path.basename(filename))[0][-5:]
|
||||
|
||||
|
||||
# Function to modify the numeric part of the filename
|
||||
def modify_filename(filename, increment=True):
|
||||
def modify_filename(filename, frame_increment=1):
|
||||
directory, basename = os.path.split(filename)
|
||||
basename_no_ext, ext = os.path.splitext(basename)
|
||||
print(f"Modifying filename {basename_no_ext} in directory {directory}.")
|
||||
if len(basename_no_ext) < 4 or not basename_no_ext[-4:].isdigit():
|
||||
if len(basename_no_ext) < 5 or not basename_no_ext[-5:].isdigit():
|
||||
raise ValueError("Filename does not end with five digits.")
|
||||
num_part = basename_no_ext[-4:]
|
||||
num = int(num_part) + (1 if increment else -1)
|
||||
new_name = f"{basename_no_ext[:-4]}{num:04d}{ext}"
|
||||
|
||||
num_part = basename_no_ext[-5:]
|
||||
num = int(num_part) + frame_increment
|
||||
|
||||
# Handle rollover
|
||||
num = num % 100000 # Modulo 100000 for 5 digits
|
||||
|
||||
new_name = f"{basename_no_ext[:-5]}{num:05d}{ext}"
|
||||
new_path = os.path.join(directory, new_name)
|
||||
if not os.path.exists(new_path):
|
||||
print(f"No file found at {new_path}.")
|
||||
|
@ -80,6 +61,49 @@ def modify_filename(filename, increment=True):
|
|||
return new_path
|
||||
|
||||
|
||||
# Function to display the image and pixel values along with the frame index
|
||||
def show_pixel_values(image_path):
|
||||
global img, calibrated_img, last_x, last_y
|
||||
|
||||
def mouse_event(event, x, y, flags, param):
|
||||
global last_x, last_y
|
||||
if event == cv2.EVENT_MOUSEMOVE:
|
||||
last_x, last_y = x, y
|
||||
update_display(x, y)
|
||||
|
||||
img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
|
||||
if img is None:
|
||||
print(f"Failed to load image at {image_path}. Check the file path and integrity.")
|
||||
return False
|
||||
|
||||
calibrated_img = calibrate(img) # Calibrate the image for display
|
||||
|
||||
cv2.namedWindow('Image')
|
||||
cv2.setMouseCallback('Image', mouse_event)
|
||||
update_display(last_x, last_y) # Initial display update
|
||||
return True
|
||||
|
||||
# Function to update the display with pixel values
|
||||
def update_display(x, y):
|
||||
global img, calibrated_img
|
||||
original_pixel_value = img[y, x]
|
||||
calibrated_pixel_value = calibrated_img[y, x]
|
||||
text_original = f'Original: {original_pixel_value}, Loc: ({x},{y})'
|
||||
text_calibrated = f'Calibrated: {calibrated_pixel_value}'
|
||||
img_text = img.copy()
|
||||
frame_index = get_frame_index(img_path)
|
||||
cv2.putText(img_text, f'Frame: {frame_index}', (10, img_text.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1, cv2.LINE_AA)
|
||||
cv2.putText(img_text, text_original, (5, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1, cv2.LINE_AA)
|
||||
cv2.putText(img_text, text_calibrated+"c", (5, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1, cv2.LINE_AA)
|
||||
cv2.imshow('Image', img_text)
|
||||
return img_text # Return the image with text for saving
|
||||
|
||||
def save_frame(img_text):
|
||||
current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
save_path = f"frame_{current_time}.png"
|
||||
cv2.imwrite(save_path, img_text)
|
||||
print(f"Frame saved as {save_path}")
|
||||
|
||||
# Ensure the provided path is a valid file
|
||||
if not os.path.isfile(img_path):
|
||||
print("The provided path is not a valid file.")
|
||||
|
@ -94,15 +118,25 @@ while True:
|
|||
key = cv2.waitKey(0)
|
||||
if key == 27: # ESC key to exit
|
||||
break
|
||||
elif key == 91: # '[' key
|
||||
img_path = modify_filename(img_path, increment=False)
|
||||
elif key == 93: # ']' key
|
||||
img_path = modify_filename(img_path, increment=True)
|
||||
elif key == ord('c'):
|
||||
state.calibrate = not state.calibrate
|
||||
elif key in [91, 93, ord('{'), ord('}')]: # Keys for frame navigation
|
||||
if key == 91: # '[' key
|
||||
img_path = modify_filename(img_path, frame_increment=-1)
|
||||
elif key == 93: # ']' key
|
||||
img_path = modify_filename(img_path, frame_increment=1)
|
||||
elif key == ord('{'): # Shift + '['
|
||||
img_path = modify_filename(img_path, frame_increment=-50)
|
||||
elif key == ord('}'): # Shift + ']'
|
||||
img_path = modify_filename(img_path, frame_increment=50)
|
||||
|
||||
if not show_pixel_values(img_path):
|
||||
break # Exit if the new image cannot be loaded
|
||||
else:
|
||||
update_display(last_x, last_y) # Update display with last known mouse position
|
||||
|
||||
# Show the new image
|
||||
if not show_pixel_values(img_path):
|
||||
break # Exit the loop if the new image cannot be loaded
|
||||
elif key == ord('s'): # 's' key for saving
|
||||
# Update the display to get the latest overlay and save it
|
||||
img_text_with_overlays = update_display(last_x, last_y)
|
||||
save_frame(img_text_with_overlays)
|
||||
continue # Skip the frame reload if saving
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
cv2.destroyAllWindows()
|
|
@ -6,7 +6,6 @@ fn main() -> anyhow::Result<()> {
|
|||
let mut arg = env::args();
|
||||
arg.next(); // skip executable
|
||||
let filename = arg.next().ok_or(anyhow::anyhow!("unexpected"))?;
|
||||
let frames = arg.next().unwrap_or("frames".into());
|
||||
decode_to_files(&filename, &frames)?;
|
||||
decode_to_files(&filename)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
2
rustdecode.sh
Executable file
2
rustdecode.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
cargo run --release --example main -- "$@"
|
17
src/lib.rs
17
src/lib.rs
|
@ -251,15 +251,26 @@ fn decode(filename: &str) -> PyResult<PyFrameIterator> {
|
|||
Ok(iter.into())
|
||||
}
|
||||
|
||||
pub fn decode_to_files(filename: &str, frames_root: &str) -> anyhow::Result<()> {
|
||||
/// writes to frames/<basename of filename>
|
||||
pub fn decode_to_files(filename: &str) -> anyhow::Result<()> {
|
||||
let frameiter = Decoder::new(filename)?;
|
||||
let basename = std::path::Path::new(filename)
|
||||
.file_stem()
|
||||
.ok_or(anyhow::anyhow!("cannot get basename"))?
|
||||
.to_str()
|
||||
.ok_or(anyhow::anyhow!("cannot convert to utf-8 from os name"))?;
|
||||
let target_dir = format!("frames/{}", basename);
|
||||
let target_dir = std::path::Path::new(&target_dir);
|
||||
if !target_dir.exists() {
|
||||
std::fs::create_dir(target_dir)?;
|
||||
}
|
||||
for (i, frame) in frameiter.enumerate() {
|
||||
let name = format!("{}/{:05}.png", frames_root, i);
|
||||
let name = format!("frames/{}/{:05}.png", basename, i);
|
||||
if let Err(_e) = write_raw_frame(&name, &frame.raw) {
|
||||
println!("skipping bad frame {}", i);
|
||||
continue;
|
||||
}
|
||||
let name = format!("{}/temp_{:05}.png", frames_root, i);
|
||||
let name = format!("{}/temp_{:05}.png", target_dir.display(), i);
|
||||
let pixels = frame.pixels();
|
||||
write_calibrated_frame(&name, &pixels)?;
|
||||
}
|
||||
|
|
BIN
thermal.mp4
BIN
thermal.mp4
Binary file not shown.
Loading…
Reference in New Issue
Block a user