Repository navigation
Conversation
Step 3 writes two additional columns, ob_sum_col_value and ob_noise_sum_col_value: the integrated LED intensity with the black level taken from the optical black margin of the sensor and without clipping at zero, and its read noise. sum_col_val keeps a constant offset from the metadata black level and the clipping at zero (0.3-0.7 % of I0 in the Warsaw tests), which saturates the extinction coefficients in dense smoke. sum_col_val and its column layout are unchanged. New options of the linear solver (config_analysis.ini): - reference_property = ob_sum_col_val - snr_full_weight / min_layer_weight: weight every LED with min(1, SNR / snr_full_weight); layers whose path-weighted mean LED weight is below min_layer_weight are set to NaN - num_avg_images: centred moving average of the reference property over N images before the normalisation; reference intensities from the single images - dark_img_indices: subtract a per-LED dark level taken from images with LEDs and ambient light off (diagnostic, superseded by ob_sum_col_val) Not covered: nonlinear solver, stacked analysis. Checked on the Warsaw tests V008 and V009 (camera 2) against BCL. Unit tests pass, acceptance tests not run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019vfEYDVyW5dLrb84n1nqns
The side band pedestal of #145 and the optical black intensity of this branch both extend the step 3 output and the raw reader, so all five conflicts sit in the files #145 touched. - Columns: the side band columns keep their place and ob_sum_col_value and ob_noise_sum_col_value are appended after them, so the column prefix of the released v0.11.0 files stays as it is. The layouts of -s3_fast and -s3 now have 12 and 26 names. Step 3 files written by this branch before the merge (9 and 23 names) can no longer be read. - Reader: step 3 reads through read_channel_data_and_optical_black_from_img and takes the channel mask of the side bands from the Bayer filter array, so the image is still read once for both corrections. The reader now returns the filter array for every RAW file and not only for sensors with an optical black margin, because a filter array of None means "every pixel belongs to the channel" to the side bands. - _channel_mask of this branch was dropped for get_bayer_channel_mask, which master made public with the same body. - The NaN fixture of test_sideband_background builds the solver without __init__, so it now sets the attributes load_img_data gained here: noise_property, dark_img_indices and num_avg_images. read_channel_data_and_mask_from_img has no caller in the package any more, only its own unit tests, since step 3 reads through the optical black version. Unit tests pass, 117. Acceptance tests not run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every RAW reader now takes the black level of a channel from the mean of the optical black pixels of the same image instead of raw.black_level_per_channel. The metadata value is a nominal one of the camera model, while the optical black pixels are masked pixels of the same sensor read out with the image itself, so their mean also follows the drift of the black level with the sensor temperature. The two differ by a fraction of a count, which the integration over the search area of an LED turns into a constant offset of its intensity, and that offset does not vanish when the LED dims in dense smoke. The metadata value stays as the fallback for sensors whose optical black margin is narrower than MIN_OPTICAL_BLACK_WIDTH, resolved in one place for all readers (_black_level_and_noise). JPG and PNG files have no black level and are untouched. This changes sum_col_val, bgsub_sum_col_val, sidebg_sum_col_val and bgsub2_sum_col_val of RAW images, so the step 3 header now records the LEDSA version, as the headers of the analysis results already do. The columns alone no longer say how a file was read. ob_sum_col_val keeps its own per-color black level and remains the only column without the clipping at zero, so the difference between it and sum_col_val is now the clipping alone. Unit tests pass, 127, including the new test_black_level.py for the estimate and the fallback. Acceptance tests not run, and no RAW image is covered by the unit tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
create_binary_data wrote all_parameters.h5 with append=True and nothing removed the file of an earlier run, so every LED ended up in the table once per run and the analysis saw each image as often as the step was repeated. The binary data are derived from the step 3 result files, so they are now rewritten from scratch and the replacement is reported. The column names are taken from the first result file of a channel and used for all of them, so result files of different width cannot be loaded together. That happens when step 3 is run again for part of the images after a LEDSA version that writes further columns was installed. Until now pandas stopped with "Shape of passed values is (n, 12), indices imply (n, 10)", which says nothing about the cause. The width of every file is now compared with the one of the first file and the error names both files, both counts and the re-run of the data extraction. Unit tests pass, 133, with six new ones in test_file_handling.py. They write a coordinate file, so they take the same path through _append_coordinates as a real analysis. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dark_img_indices subtracted a per-LED dark level taken from images with the LEDs and the ambient light switched off. ob_sum_col_val takes the same information from the optical black pixels of every image, per Bayer color and without extra exposures at the experiment, so the option is superseded, as its own commit message already said. It spanned seven files with a solver attribute, subtract_dark_intensities and an option of config_analysis.ini, and it reached into a test fixture that has nothing to do with dark images. read_channel_data_and_mask_from_img has no caller left, because step 3 reads through read_channel_data_and_optical_black_from_img, which returns the Bayer filter array the side bands need. Its three unit tests now cover that reader instead: the mask that keeps channel pixels of value 0, the filter array of None that a JPG or PNG yields, and the error for an unsupported extension. The first one drives the reader through a fake RAW image now, so it no longer mocks away the function under test. Unit tests pass, 133. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The CI check for text I/O without an encoding is line based, so the encoding of a wrapped call reads as missing to it although Python sees it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Hi Kristian, thanks for merging #145 and for the follow-up in #150. One of the points you left open there is that values at What it does.
S0, P0 and S0_corr are means over the reference images. The threshold (0.1), the minimum optical depth from which the relative criteria apply (0.1) and U_B (0.2) are set values, not derived ones. On one camera of one of our Hasenheide runs, the marking agrees with that of our own evaluation scripts on all 216,384 rays, for thresholds from 0.05 to 0.2 and U_B from 0.1 to 0.3. That confirms the implementation, not the criterion itself. Relation to #157. It addresses two points you name there:
It is a marking after the fact and does not weight the inversion. Options I see:
Which would you prefer? On the other open point, mean vs. trimmed mean in the side bands, I could compare both estimators on our Hasenheide data, if that helps. Best, |
|
Replaced by #169, which re-cuts this work onto current master. This branch had drifted eleven conflicting files away from master: the finished half merged as #158, and #161 through #167 landed after the merge commit it carries. Rebuilding the remainder on top of master was cleaner than resolving that, as it was for #158. Carried over: the unclipped Dropped for good: The branch stays as it is, so nothing is lost. |
Draft for the open half of the dark level correction. The finished half was split out into #158, which should merge first. This branch still contains it, so its diff will shrink to the remainder once #158 is in master.
What belongs here
ob_sum_col_valueandob_noise_sum_col_value: the integrated intensity of an LED without the clipping at zero, with the black level of every color of the Bayer pattern taken individually, and the read noise of that sum from the optical black.snr_full_weightandmin_layer_weight: the linear solver weights every LED withmin(1, SNR / snr_full_weight), and layers whose path-length-weighted mean LED weight stays belowmin_layer_weightbecome NaN instead of resting on LEDs near the noise level.num_avg_images: centred moving average of the reference property over N images before the normalisation, with the reference intensities taken from the single images.sum_col_valand the side band columns profit from it as well and not only an opt-in column.What moved to #158
The merge of master, the optical black as the mandatory black level source with the fallback to the metadata value, the LEDSA version in the step 3 header, and the binary data guard. None of that has an open question, and it improves every reference property rather than an opt-in one.
What was dropped
dark_img_indices, which subtracted a per-LED dark level taken from images with the LEDs and the ambient light switched off.ob_sum_col_valtakes the same information from the optical black pixels of every image, per Bayer color and without extra exposures at the experiment, so the option was superseded by its own commit message. It spanned seven files, a solver attribute,subtract_dark_intensitiesand an option ofconfig_analysis.ini.read_channel_data_and_mask_from_img, which has no caller left because step 3 reads throughread_channel_data_and_optical_black_from_img. Its three unit tests now cover that reader instead, the first one driving it through a fake RAW image rather than mocking the function under test.Why this stays a draft
max_col_value, the fit and the postprocessing rely on, and the nonlinear solver and the stacked analysis are not covered.subtract_pedestalclamps tomax(S - N * B, background_floor * S)and its own docstring says values at the limit carry no information on the extinction, yet nothing marks them. The answer here is the opposite: keep the negative value and discard it by SNR. Combined, the floor must not clamp a sum based on the optical black. Today the two cannot meet, becauseNOISE_COLUMNSwires the SNR weighting toob_sum_col_valalone and that column never goes throughsubtract_pedestal.ob_noise_sum_col_valueis the read noise of the box only. Subtracting a side band pedestal adds the variance of that estimate, and the side bands hold far fewer pixels than the box, so the weighting would be optimistic exactly in dense smoke.Note on the merge state
This branch conflicts with master in four files, because #155 and #156 landed after the merge commit it carries. That is left as it is on purpose: merging master again now would duplicate the resolution that #158 already contains. Once #158 is merged, master comes in here and the conflicts reduce to the parts this branch really changes.
🤖 Generated with Claude Code