Skip to content

Commit 919326b

Browse files
kalidkeclaude
andauthored
Fix SCMOSCamera matrix convention to match Julia standard (#34)
* Fix SCMOSCamera matrix convention to match Julia standard The validation check for calibration matrices (readnoise, gain, offset, qe) was expecting size (nx, ny) but Julia arrays use [row, col] indexing, so matrices should be (ny, nx) where ny=rows and nx=cols. This bug was masked by square camera tests but would cause: - BoundsError for rectangular cameras when downstream code accesses map[y, x] - Semantic errors for square cameras (data transposed) Changes: - Fix _validate_camera_param to expect (ny, nx) instead of (nx, ny) - Update docstrings to clarify the convention - Fix existing rectangular test that used wrong convention - Add explicit test verifying semantic access pattern Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Bump version to 0.5.1 --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e9cf0ac commit 919326b

File tree

3 files changed

+33
-8
lines changed

3 files changed

+33
-8
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SMLMData"
22
uuid = "5488f106-40b8-4660-84c5-84a168990d1b"
33
authors = ["klidke@unm.edu"]
4-
version = "0.5.0"
4+
version = "0.5.1"
55

66
[deps]
77
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"

src/types/cameras.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ Construct sCMOS camera from pixel dimensions and calibration parameters.
422422
- `gain::Union{T, Matrix{T}} = 1`: Conversion gain in e⁻/ADU
423423
- `qe::Union{T, Matrix{T}} = 1`: Quantum efficiency (0-1)
424424
425-
Each parameter can be scalar (uniform) or Matrix{T} with size (nx, ny).
425+
Each parameter can be scalar (uniform) or Matrix{T} with size (ny, nx) following Julia's [row, col] convention.
426426
427427
# Examples
428428
```julia
@@ -483,7 +483,7 @@ Construct sCMOS camera with custom pixel edge positions.
483483
- `gain::Union{T, Matrix{T}} = 1`: Conversion gain in e⁻/ADU
484484
- `qe::Union{T, Matrix{T}} = 1`: Quantum efficiency (0-1)
485485
486-
Matrix parameters must have size (nx, ny) where nx = length(pixel_edges_x) - 1.
486+
Matrix parameters must have size (ny, nx) following Julia's [row, col] convention, where nx = length(pixel_edges_x) - 1 and ny = length(pixel_edges_y) - 1.
487487
488488
# Example
489489
```julia
@@ -515,9 +515,11 @@ function SCMOSCamera(
515515
end
516516

517517
# Validation helper
518+
# Matrix parameters use Julia standard (row, col) = (y, x) convention
519+
# So for a camera with nx columns and ny rows, matrices should be (ny, nx)
518520
function _validate_camera_param(param::AbstractMatrix, nx, ny, name)
519-
size(param) == (nx, ny) ||
520-
throw(DimensionMismatch("$name size $(size(param)) must match ($nx, $ny)"))
521+
size(param) == (ny, nx) ||
522+
throw(DimensionMismatch("$name size $(size(param)) must match ($ny, $nx) [rows, cols]"))
521523
end
522524
_validate_camera_param(param::Real, nx, ny, name) = nothing
523525

test/test_cameras.jl

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,12 @@ end
252252
@test cam.readnoise === 1.5
253253
@test cam.gain === 0.5
254254

255-
# Matrix parameters
256-
noise_map = ones(Float64, 10, 5) .* 1.2
255+
# Matrix parameters - size must be (ny, nx) = (rows, cols) following Julia convention
256+
# edges_x has 11 elements → nx = 10 columns
257+
# edges_y has 6 elements → ny = 5 rows
258+
noise_map = ones(Float64, 5, 10) .* 1.2 # (ny, nx) = (5, 10)
257259
cam2 = SCMOSCamera(edges_x, edges_y, readnoise=noise_map)
258-
@test size(cam2.readnoise) == (10, 5)
260+
@test size(cam2.readnoise) == (5, 10)
259261
end
260262

261263
@testset "Type stability" begin
@@ -286,6 +288,27 @@ end
286288
@test_throws DimensionMismatch SCMOSCamera(10, 10, 0.1, 1.5, qe=wrong_size_map)
287289
end
288290

291+
@testset "Matrix convention (ny, nx) for rectangular cameras" begin
292+
# Rectangular camera: 512 columns (x), 256 rows (y)
293+
# Matrix must be (ny, nx) = (256, 512) following Julia [row, col] convention
294+
nx, ny = 512, 256
295+
296+
# Create noise map with a marker at known position
297+
noise_map = ones(Float64, ny, nx) # (256, 512) = (rows, cols)
298+
noise_map[100, 300] = 5.0 # row 100, col 300 → pixel (x=300, y=100)
299+
300+
cam = SCMOSCamera(nx, ny, 0.1, noise_map)
301+
@test size(cam.readnoise) == (ny, nx)
302+
@test size(cam.readnoise) == (256, 512)
303+
304+
# Verify semantic access: map[y, x] gives value for pixel at (x, y)
305+
@test cam.readnoise[100, 300] == 5.0
306+
307+
# Transposed matrix should fail
308+
wrong_map = ones(Float64, nx, ny) # (512, 256) - WRONG
309+
@test_throws DimensionMismatch SCMOSCamera(nx, ny, 0.1, wrong_map)
310+
end
311+
289312
@testset "Realistic use cases" begin
290313
# ORCA-Flash4.0 V3
291314
cam_flash = SCMOSCamera(

0 commit comments

Comments
 (0)