Skip to content

Commit 1ef4a31

Browse files
committed
Reworked the capture resoltuion situation and added DNG raw support
1 parent f7ef4b1 commit 1ef4a31

File tree

6 files changed

+91
-50
lines changed

6 files changed

+91
-50
lines changed

app.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from picamera2 import Picamera2
1010
from picamera2.encoders import JpegEncoder
11+
from picamera2.encoders import MJPEGEncoder
1112
from picamera2.outputs import FileOutput
1213
from libcamera import Transform, controls
1314

@@ -33,13 +34,14 @@
3334
selected_resolution = capture_settings["Resolution"]
3435
resolution = capture_settings["available-resolutions"][selected_resolution]
3536
print(capture_settings)
37+
print(resolution)
3638

3739
# Get the sensor modes and pick from the the camera_config
3840
camera_modes = picam2.sensor_modes
3941
mode = picam2.sensor_modes[sensor_mode]
4042

4143
# Create the video_config
42-
video_config = picam2.create_video_configuration(main={'size': mode['size']}, sensor={'output_size': mode['size'], 'bit_depth': mode['bit_depth']})
44+
video_config = picam2.create_video_configuration(main={'size':resolution}, sensor={'output_size': mode['size'], 'bit_depth': mode['bit_depth']})
4345
print(video_config)
4446

4547
# Pull default settings and filter live_settings for anything picamera2 wont use (because the not all cameras use all settings)
@@ -137,7 +139,7 @@ def video_feed():
137139
# Route to update settings to the buffer
138140
@app.route('/update_live_settings', methods=['POST'])
139141
def update_settings():
140-
global live_settings, capture_settings, picam2, video_config, resolution, sensor_mode
142+
global live_settings, capture_settings, picam2, video_config, resolution, sensor_mode, mode
141143
try:
142144
# Parse JSON data from the request
143145
data = request.get_json()
@@ -160,15 +162,21 @@ def update_settings():
160162
capture_settings['Resolution'] = int(data[key])
161163
selected_resolution = int(data[key])
162164
resolution = capture_settings["available-resolutions"][selected_resolution]
163-
return jsonify(success=True, message="Settings updated successfully", settings=live_settings)
165+
stop_camera_stream()
166+
video_config = picam2.create_video_configuration(main={'size':resolution}, sensor={'output_size': mode['size'], 'bit_depth': mode['bit_depth']})
167+
start_camera_stream()
168+
return jsonify(success=True, message="Settings updated successfully", settings=capture_settings)
169+
elif key in ('makeRaw'):
170+
capture_settings[key] = data[key]
171+
return jsonify(success=True, message="Settings updated successfully", settings=capture_settings)
164172
elif key == ('sensor_mode'):
165173
sensor_mode = int(data[key])
166174
mode = picam2.sensor_modes[sensor_mode]
167175
stop_camera_stream()
168-
video_config = picam2.create_video_configuration(main={'size': mode['size']}, sensor={'output_size': mode['size'], 'bit_depth': mode['bit_depth']})
176+
video_config = picam2.create_video_configuration(main={'size':resolution}, sensor={'output_size': mode['size'], 'bit_depth': mode['bit_depth']})
169177
start_camera_stream()
170178
save_sensor_mode(sensor_mode)
171-
return jsonify(success=True, message="Settings updated successfully", settings=live_settings)
179+
return jsonify(success=True, message="Settings updated successfully", settings=sensor_mode)
172180
except Exception as e:
173181
return jsonify(success=False, message=str(e))
174182

@@ -269,10 +277,7 @@ def save_sensor_mode(sensor_mode):
269277
####################
270278

271279
def start_camera_stream():
272-
global picam2, output, video_config, still_config
273-
#video_config = picam2.create_video_configuration()
274-
# Flip Camera #################### Make configurable
275-
# video_config["transform"] = Transform(hflip=1, vflip=1)
280+
global picam2, output, video_config
276281
picam2.configure(video_config)
277282
output = StreamingOutput()
278283
picam2.start_recording(JpegEncoder(), FileOutput(output))
@@ -298,17 +303,18 @@ def take_photo():
298303
global picam2, capture_settings
299304
try:
300305
timestamp = int(datetime.timestamp(datetime.now()))
301-
image_name = f'pimage_{timestamp}.jpg'
306+
image_name = f'pimage_{timestamp}'
302307
filepath = os.path.join(app.config['UPLOAD_FOLDER'], image_name)
303308
request = picam2.capture_request()
304-
request.save("main", filepath)
305-
309+
request.save("main", f'{filepath}.jpg')
310+
if capture_settings["makeRaw"]:
311+
request.save_dng(f'{filepath}.dng')
306312
request.release()
307-
selected_resolution = capture_settings["Resolution"]
308-
resolution = capture_settings["available-resolutions"][selected_resolution]
309-
original_image = Image.open(filepath)
310-
resized_image = original_image.resize(resolution)
311-
resized_image.save(filepath)
313+
#selected_resolution = capture_settings["Resolution"]
314+
#resolution = capture_settings["available-resolutions"][selected_resolution]
315+
#original_image = Image.open(filepath)
316+
#resized_image = original_image.resize(resolution)
317+
#resized_image.save(filepath)
312318
logging.info(f"Image captured successfully. Path: {filepath}")
313319
except Exception as e:
314320
logging.error(f"Error capturing image: {e}")
@@ -337,24 +343,31 @@ def restart_configure_camera(restart_settings):
337343
# Image Gallery Functions
338344
####################
339345

346+
from datetime import datetime
347+
import os
348+
340349
@app.route('/image_gallery')
341350
def image_gallery():
342351
try:
343-
image_files = [f for f in os.listdir(UPLOAD_FOLDER) if f.endswith(('.jpg', '.jpeg', '.png', '.gif'))]
352+
image_files = [f for f in os.listdir(UPLOAD_FOLDER) if f.endswith(('.jpg'))]
344353

345354
if not image_files:
346355
# Handle the case where there are no files
347356
return render_template('no_files.html')
348357

349-
# Create a list of dictionaries containing file name and timestamp
358+
# Create a list of dictionaries containing file name, timestamp, and dng presence
350359
files_and_timestamps = []
351360
for image_file in image_files:
352361
# Extracting Unix timestamp from the filename
353362
unix_timestamp = int(image_file.split('_')[-1].split('.')[0])
354363
timestamp = datetime.utcfromtimestamp(unix_timestamp).strftime('%Y-%m-%d %H:%M:%S')
355364

365+
# Check if corresponding .dng file exists
366+
dng_file = os.path.splitext(image_file)[0] + '.dng'
367+
has_dng = os.path.exists(os.path.join(UPLOAD_FOLDER, dng_file))
368+
356369
# Appending dictionary to the list
357-
files_and_timestamps.append({'filename': image_file, 'timestamp': timestamp})
370+
files_and_timestamps.append({'filename': image_file, 'timestamp': timestamp, 'has_dng': has_dng, 'dng_file': dng_file})
358371

359372
# Sorting the list based on Unix timestamp
360373
files_and_timestamps.sort(key=lambda x: x['timestamp'], reverse=True)
@@ -364,6 +377,7 @@ def image_gallery():
364377
logging.error(f"Error loading image gallery: {e}")
365378
return render_template('error.html', error=str(e))
366379

380+
367381
@app.route('/delete_image/<filename>', methods=['DELETE'])
368382
def delete_image(filename):
369383
try:

camera-config.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
},
3232
"sensor-mode": 2,
3333
"capture-settings": {
34+
"Resize": false,
35+
"makeRaw": true,
3436
"Resolution": 0,
3537
"available-resolutions": [
3638
[

static/gallery/pimage_1709139459.jpg

65.3 KB
Loading

templates/camera_info.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,7 @@ <h2 class="fw-bold text-body-emphasis">{{ connected_camera_data.module_name }}</
192192
{% endif %}
193193
</div>
194194
</div>
195-
196195

197-
198-
199-
200196

201197

202198
<script>

templates/camerasettings.html

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ <h2 class="accordion-header">
396396
</div>
397397
</div>
398398
</div>
399-
<!-- ###### Auto White Balance Settings ###### -->
399+
<!-- ###### Camera Capture Settings ###### -->
400400
<div class="accordion-item">
401401
<h2 class="accordion-header">
402402
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSix" aria-expanded="false" aria-controls="collapseSix">
@@ -406,27 +406,33 @@ <h2 class="accordion-header">
406406
<div id="collapseSix" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
407407
<div class="accordion-body">
408408
<!-- Resolution -->
409-
<p>Select Resolution:</p>
409+
<p>Select Feed/Capture Resolution:</p>
410410
<div class="form-check">
411-
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution0" onclick="adjustCheckboxSetting('Resolution', '0')">
411+
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution0" onclick="adjustResoltuion('Resolution', '0')">
412412
<label class="form-check-label" for="Resolution0">4608, 2592</label>
413413
</div>
414414
<div class="form-check">
415-
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution1" onclick="adjustCheckboxSetting('Resolution', '1')">
415+
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution1" onclick="adjustResoltuion('Resolution', '1')">
416416
<label class="form-check-label" for="Resolution1">2304, 1296</label>
417417
</div>
418418
<div class="form-check">
419-
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution2" onclick="adjustCheckboxSetting('Resolution', '2')">
419+
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution2" onclick="adjustResoltuion('Resolution', '2')">
420420
<label class="form-check-label" for="Resolution2">1920, 1080</label>
421421
</div>
422422
<div class="form-check">
423-
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution3" onclick="adjustCheckboxSetting('Resolution', '3')">
423+
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution3" onclick="adjustResoltuion('Resolution', '3')">
424424
<label class="form-check-label" for="Resolution3">1280, 720</label>
425425
</div>
426426
<div class="form-check">
427-
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution4" onclick="adjustCheckboxSetting('Resolution', '4')">
427+
<input class="form-check-input" type="checkbox" name="Resolution" id="Resolution4" onclick="adjustResoltuion('Resolution', '4')">
428428
<label class="form-check-label" for="Resolution4">640, 360</label>
429429
</div>
430+
<hr></hr>
431+
<!-- Make Raw -->
432+
<div class="form-check form-switch">
433+
<input class="form-check-input" type="checkbox" role="switch" id="makeRaw" onchange="adjustSwitchSetting('makeRaw')">
434+
<label for="makeRaw" >Enable: Save Raw Image</label>
435+
</div>
430436
<!-- End of Line-->
431437
</div>
432438
</div>
@@ -486,7 +492,7 @@ <h2 class="accordion-header">
486492
});
487493

488494
const checkboxSettings = ['AfMode', 'AfRange', 'AfSpeed', 'AeConstraintMode', 'AeExposureMode', 'AeFlickerMode', 'AeMeteringMode', 'AwbMode', 'Resolution'];
489-
const switchSettings = ['AwbEnable', 'AeEnable', 'AwbMode', 'hflip', 'vflip'];
495+
const switchSettings = ['AwbEnable', 'AeEnable', 'AwbMode', 'hflip', 'vflip', 'makeRaw'];
490496
const sliderSettings = ['LensPosition', 'ExposureValue', 'Brightness', 'Contrast', 'Saturation', 'Sharpness'];
491497
const inputSettings = ['ExposureTime', 'AeFlickerPeriod']
492498

@@ -661,12 +667,16 @@ <h2 class="accordion-header">
661667
function adjustCheckboxSetting(settingId, selection) {
662668
// Convert settingValue to an integer
663669
const settingValue = parseInt(selection);
670+
664671
// Update UI with the new value
665672
updateUI({ [settingId]: settingValue });
666-
// Update server settings
667-
updateLiveSettings({ [settingId]: settingValue });
668-
// Notify console
669-
console.log(settingId, 'changed:', settingValue);
673+
674+
// Update server settings and return the promise
675+
return updateLiveSettings({ [settingId]: settingValue })
676+
.then(() => {
677+
// Notify console
678+
console.log(settingId, 'changed:', settingValue);
679+
});
670680
}
671681

672682
function adjustSliderSetting(settingId, selection) {
@@ -880,6 +890,22 @@ <h2 class="accordion-header">
880890
}
881891
}
882892

893+
function adjustResoltuion(settingId, selection) {
894+
// Call adjustCheckboxSetting
895+
adjustCheckboxSetting(settingId, selection)
896+
.then(() => {
897+
// The asynchronous part is done, now you can perform other tasks
898+
refreshCameraFeed();
899+
})
900+
.catch(error => {
901+
console.error('Error updating server settings:', error);
902+
// Handle the error if needed
903+
});
904+
}
905+
906+
907+
908+
883909
function adjustflip(mode) {
884910
// Convert mode to an integer
885911
const hflipswitch = document.getElementById('hflip');

templates/image_gallery.html

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,26 @@ <h5 class="modal-title" id="deleteConfirmationModalLabel">Confirm Deletion</h5>
3131
<div class="container">
3232
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
3333
{% for file_data in image_files %}
34-
<div class="col" id="card_{{ file_data['filename'] }}">
35-
<div class="card shadow-sm" >
36-
<img src="{{ url_for('static', filename='gallery/' + file_data['filename']) }}" alt="{{ file_data['filename'] }}" class="bd-placeholder-img card-img-top" width="100%">
37-
<div class="card-body">
38-
<p class="card-text">Date taken: {{ file_data['timestamp'] }}</p>
39-
<div class="d-flex justify-content-between align-items-center">
40-
<div class="btn-group">
41-
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="window.location.href='/view_image/{{ file_data['filename'] }}'">View</button>
42-
<button type="button" class="btn btn-sm btn-outline-danger" onclick="openDeleteConfirmationModal('{{ file_data['filename'] }}', 'card_{{ file_data['filename'] }}')">Delete</button>
43-
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="window.location.href='/download_image/{{ file_data['filename'] }}'">Download</button>
44-
45-
</div>
46-
</div>
34+
<div class="col" id="card_{{ file_data['filename'] }}">
35+
<div class="card shadow-sm" >
36+
<img src="{{ url_for('static', filename='gallery/' + file_data['filename']) }}" alt="{{ file_data['filename'] }}" class="bd-placeholder-img card-img-top" width="100%">
37+
<div class="card-body">
38+
<p class="card-text">Date taken: {{ file_data['timestamp'] }}</p>
39+
<div class="d-flex justify-content-between align-items-center">
40+
<div class="btn-group">
41+
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="window.location.href='/view_image/{{ file_data['filename'] }}'">View</button>
42+
<button type="button" class="btn btn-sm btn-outline-danger" onclick="openDeleteConfirmationModal('{{ file_data['filename'] }}', 'card_{{ file_data['filename'] }}')">Delete</button>
43+
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="window.location.href='/download_image/{{ file_data['filename'] }}'">Download</button>
44+
{% if file_data['has_dng'] %}
45+
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="window.location.href='/download_image/{{ file_data['dng_file'] }}'">Download Raw</button>
46+
{% endif %}
4747
</div>
4848
</div>
4949
</div>
50-
{% endfor %}
50+
</div>
51+
</div>
52+
{% endfor %}
53+
5154
</div>
5255
</div>
5356
</div>

0 commit comments

Comments
 (0)