Image Fitting

skinparam style strictuml
hide footbox
title Image Fitting workflow

actor User

box "Client-side" #EDEDED
        participant Frontend
end box

box "Server-side" #lightblue
    participant Backend
end box

User -> Frontend: Open image
activate Frontend
Frontend -> Backend : 1. OPEN_FILE
activate Backend
Frontend <-- Backend : 2. OPEN_FILE_ACK
Frontend -> Backend : 3. ADD_REQUIRED_TILES
Frontend -> Backend : 4. SET_CURSOR
Frontend <-- Backend : 5. RASTER_TILE_DATA
Frontend <-- Backend : 5. SPATIAL_PROFILE_DATA
deactivate Backend
User <-- Frontend: Displays image
deactivate Frontend

User -> Frontend: Request Gaussian fitting
activate Frontend
Frontend -> Backend : 6. FITTING_REQUEST
activate Backend
Frontend <-- Backend : 7. FITTING_PROGRESS (stream)
Frontend <--[#red] Backend : <font color="red">8. FITTING_RESPONSE [Check]</font>
deactivate Backend
User <-- Frontend: Displays fitting results
deactivate Frontend

IMAGE_FITTING_FITS

See the source code.

This test verifies Gaussian image fitting on FITS files, covering various configurations: without field of view (FoV), with FoV using different solvers (Cholesky, QR, SVD), creating model and residual images, region-based fitting, and fitting with background flux offset.

  1. Frontend sends: CLOSE_FILE (CloseFile)

    file_id = -1
    
  2. Frontend sends: OPEN_FILE (OpenFile)

    directory = "set_QA"
    file = "M17_SWex-channel0-addOneGaussian.fits"
    hdu = "0"
    file_id = 0
    render_mode = RASTER
    
  3. Backend returns: OPEN_FILE_ACK (OpenFileAck) and REGION_HISTOGRAM_DATA

Check 1: the OPEN_FILE_ACK should satisfy:

  • OPEN_FILE_ACK.success = True

  1. Frontend sends: ADD_REQUIRED_TILES (AddRequiredTiles)

    file_id = 0
    compression_quality = 11
    compression_type = ZFP
    tiles = [0]
    
  2. Frontend sends: SET_CURSOR (SetCursor)

    file_id = 0
    point = {x: 1, y: 1}
    
  3. Backend returns: RASTER_TILE_DATA and SPATIAL_PROFILE_DATA

Check 2: the RASTER_TILE_DATA stream should satisfy:

  • Total length = 3 (RasterTileSync start + 1 tile + RasterTileSync end)

Case 1: Image fitting without FoV

  1. Frontend sends: FITTING_REQUEST (FittingRequest)

    file_id = 0
    create_model_image = false
    create_residual_image = false
    fixed_params = [false, false, false, false, false, false, true]
    fov_info = null
    region_id = -1
    initial_values = [{amp: 10, center: {x: 320, y: 400}, fwhm: {x: 100, y: 50}, pa: 135}]
    
  2. Backend returns: FITTING_PROGRESS (stream) and FITTING_RESPONSE (FittingResponse)

Check 3: the FITTING_RESPONSE should satisfy:

success = true
result_values[0].center = {x: 319.50, y: 399.50}
result_values[0].amp = 10.00
result_values[0].fwhm = {x: 170.64, y: 41.48}
result_values[0].pa = 142.16
log contains "Gaussian fitting with 1 component"
  • resultErrors should be close to zero

Case 2-1: Image fitting with FoV (solver = Cholesky)

  1. Frontend sends: FITTING_REQUEST (FittingRequest)

    file_id = 0
    create_model_image = false
    create_residual_image = false
    fixed_params = [false, false, false, false, false, false, true]
    fov_info = {control_points: [{x: 319.5, y: 399.5}, {x: 216.71, y: 200.0}], region_type: RECTANGLE, rotation: 0}
    region_id = 0
    initial_values = [{amp: 10, center: {x: 320, y: 400}, fwhm: {x: 100, y: 50}, pa: 135}]
    solver = Cholesky
    
  2. Backend returns: FITTING_RESPONSE (FittingResponse)

Check 4: the FITTING_RESPONSE should satisfy:

success = true
result_values[0].center = {x: 319.50, y: 399.50}
result_values[0].amp = 10.00
result_values[0].fwhm = {x: 170.64, y: 41.48}
result_values[0].pa = 142.16

Case 2-2: Image fitting with FoV (solver = QR)

  1. Frontend sends: FITTING_REQUEST with solver = QR

  2. Backend returns: FITTING_RESPONSE (FittingResponse)

Check 5: the FITTING_RESPONSE should match Case 2-1 results

Case 2-3: Image fitting with FoV (solver = SVD)

  1. Frontend sends: FITTING_REQUEST with solver = SVD

  2. Backend returns: FITTING_RESPONSE (FittingResponse)

Check 6: the FITTING_RESPONSE should match Case 2-1 results

Case 3: Image fitting creating model image

  1. Frontend sends: FITTING_REQUEST (FittingRequest)

    file_id = 0
    create_model_image = true
    create_residual_image = false
    fixed_params = [false, false, false, false, false, false, true]
    fov_info = null
    region_id = -1
    initial_values = [{amp: 10, center: {x: 320, y: 400}, fwhm: {x: 100, y: 50}, pa: 135}]
    
  2. Backend returns: FITTING_RESPONSE and REGION_HISTOGRAM_DATA (for model image)

Check 7: the FITTING_RESPONSE should satisfy:

  • FITTING_RESPONSE.success = True

  • REGION_HISTOGRAM_DATA.fileId = 1 (model image)

  1. Frontend sends: ADD_REQUIRED_TILES for model image (file_id = 1)

  2. Backend returns: RASTER_TILE_DATA for model image

Check 8: all RASTER_TILE_DATA should have fileId = 1

Case 4: Image fitting creating model and residual images

  1. Frontend sends: FITTING_REQUEST (FittingRequest)

    file_id = 0
    create_model_image = true
    create_residual_image = true
    region_id = -1
    
  2. Backend returns: FITTING_RESPONSE and 2 REGION_HISTOGRAM_DATA (model + residual)

Check 9: the response should satisfy:

  • FITTING_RESPONSE.success = True

  • REGION_HISTOGRAM_DATA fileIds should contain 2 and 3

Case 5: Image fitting with region, model and residual images

  1. Frontend sends: SET_REGION (SetRegion)

    file_id = 0
    region_id = 1
    region_type = RECTANGLE
    control_points = [{x: 319.5, y: 399.5}, {x: 216.71, y: 200.0}]
    rotation = 0
    
  2. Backend returns: SET_REGION_ACK (SetRegionAck)

Check 10: SET_REGION_ACK.success = True and regionId = 1

  1. Frontend sends: FITTING_REQUEST (FittingRequest)

    file_id = 0
    create_model_image = true
    create_residual_image = true
    region_id = 1
    solver = Cholesky
    offset = 0
    
  2. Backend returns: FITTING_RESPONSE and 2 REGION_HISTOGRAM_DATA

Check 11: the FITTING_RESPONSE should satisfy:

  • FITTING_RESPONSE.success = True

  • REGION_HISTOGRAM_DATA fileIds should contain 4 and 5

Case 6: Image fitting with background flux offset

  1. Frontend sends: CLOSE_FILE and opens a different file:

    file = "M17_SWex-channel0-addOneGaussian-addBackgroundFlux.fits"
    file_id = 0
    
  2. Frontend sends: SET_REGION and FITTING_REQUEST with offset = 9

  3. Backend returns: FITTING_RESPONSE

Check 12: the FITTING_RESPONSE should satisfy:

success = true
result_values[0].center = {x: 319.50, y: 399.50}
result_values[0].amp = 10.00
offset_value = 10.00
log contains "Gaussian fitting with 1 component"
  • offsetValue and offsetError should be present and valid

IMAGE_FITTING_CASA

See the source code.

This test is identical in structure to IMAGE_FITTING_FITS, but uses CASA image files (.image) instead of FITS files. It verifies that Gaussian image fitting produces the same results regardless of file format.

  1. Frontend sends: OPEN_FILE (OpenFile)

    directory = "set_QA"
    file = "M17_SWex-channel0-addOneGaussian.image"
    file_id = 0
    render_mode = RASTER
    
  2. Backend returns: OPEN_FILE_ACK (OpenFileAck) and REGION_HISTOGRAM_DATA

Check 1: the OPEN_FILE_ACK should satisfy:

  • OPEN_FILE_ACK.success = True

3-27. Same workflow as IMAGE_FITTING_FITS (Cases 1-6), using .image files

Checks 2-12: same assertions as IMAGE_FITTING_FITS

IMAGE_FITTING_HDF5

See the source code.

This test is identical in structure to IMAGE_FITTING_FITS, but uses HDF5 image files (.hdf5) instead of FITS files. It verifies that Gaussian image fitting produces the same results regardless of file format.

  1. Frontend sends: OPEN_FILE (OpenFile)

    directory = "set_QA"
    file = "M17_SWex-channel0-addOneGaussian.hdf5"
    file_id = 0
    render_mode = RASTER
    
  2. Backend returns: OPEN_FILE_ACK (OpenFileAck) and REGION_HISTOGRAM_DATA

Check 1: the OPEN_FILE_ACK should satisfy:

  • OPEN_FILE_ACK.success = True

3-27. Same workflow as IMAGE_FITTING_FITS (Cases 1-6), using .hdf5 files

Checks 2-12: same assertions as IMAGE_FITTING_FITS

IMAGE_FITTING_BAD

See the source code.

This test verifies error handling in image fitting, including cases where the solver exceeds the maximum number of iterations and where the fit does not converge.

  1. Frontend sends: CLOSE_FILE (CloseFile)

    file_id = -1
    
  2. Frontend sends: OPEN_FILE (OpenFile)

    directory = "set_QA"
    file = "ThreeComponent-inclined-2d-gaussian.fits"
    hdu = "0"
    file_id = 0
    render_mode = RASTER
    
  3. Backend returns: OPEN_FILE_ACK (OpenFileAck) and REGION_HISTOGRAM_DATA

Check 1: the OPEN_FILE_ACK should satisfy:

  • OPEN_FILE_ACK.success = True

  1. Frontend sends: ADD_REQUIRED_TILES and SET_CURSOR

  2. Backend returns: RASTER_TILE_DATA and SPATIAL_PROFILE_DATA

Case 1: Exceeded maximum number of iterations

  1. Frontend sends: FITTING_REQUEST (FittingRequest) with a 2-component Gaussian

    file_id = 0
    create_model_image = false
    create_residual_image = false
    fixed_params = [false, false, false, false, false, false, false, false, false, false, false, false, true]
    region_id = -1
    initial_values = [{amp: 1, center: {x: 164, y: 280}, fwhm: {x: 100, y: 5}, pa: 270},
                      {amp: 1, center: {x: 220, y: 270}, fwhm: {x: 30, y: 100}, pa: 45}]
    solver = Cholesky
    offset = 0
    
  2. Backend returns: FITTING_RESPONSE (FittingResponse)

Check 2: the FITTING_RESPONSE should satisfy:

  • FITTING_RESPONSE.log contains iteration exceeded message

  • FITTING_RESPONSE.message contains warning about exceeded iterations

  • Result values and errors should still be returned (platform-dependent)

Case 2: Fit did not converge

  1. Frontend sends: FITTING_REQUEST (FittingRequest) with intentionally bad initial values

    file_id = 0
    create_model_image = false
    create_residual_image = false
    fixed_params = [false, false, false, false, false, false, true]
    region_id = -1
    initial_values = [{amp: 10, center: {x: 1000, y: 280}, fwhm: {x: 100, y: 5}, pa: 270}]
    solver = Cholesky
    offset = 0
    
  2. Backend returns: FITTING_RESPONSE (FittingResponse)

Check 3: the FITTING_RESPONSE should satisfy:

  • The request should throw an error with a “did not converge” message

IMAGE_FITTING_CANCEL

See the source code.

This test verifies that an in-progress image fitting request can be cancelled via STOP_FITTING, and that a subsequent fitting request still succeeds.

  1. Frontend sends: CLOSE_FILE (CloseFile)

    file_id = -1
    
  2. Frontend sends: OPEN_FILE (OpenFile)

    directory = "set_QA"
    file = "M17_SWex-channel0-addOneGaussian.fits"
    hdu = "0"
    file_id = 0
    render_mode = RASTER
    
  3. Backend returns: OPEN_FILE_ACK (OpenFileAck) and REGION_HISTOGRAM_DATA

Check 1: the OPEN_FILE_ACK should satisfy:

  • OPEN_FILE_ACK.success = True

  1. Frontend sends: ADD_REQUIRED_TILES and SET_CURSOR

  2. Backend returns: RASTER_TILE_DATA and SPATIAL_PROFILE_DATA

  3. Frontend sends: SET_REGION (SetRegion)

    file_id = 0
    region_id = 1
    region_type = RECTANGLE
    control_points = [{x: 324, y: 398}, {x: 270, y: 270}]
    rotation = 0
    

Check 2: SET_REGION_ACK.success = True and regionId = 1

Step 1: Request fitting, then cancel

  1. Frontend sends: FITTING_REQUEST (FittingRequest)

    file_id = 0
    create_model_image = false
    create_residual_image = false
    fixed_params = [false, false, false, false, false, false, true]
    region_id = 1
    initial_values = [{amp: 10, center: {x: 320, y: 400}, fwhm: {x: 100, y: 50}, pa: 135}]
    solver = Cholesky
    offset = 0
    
  2. Backend streams: FITTING_PROGRESS (FittingProgress)

  3. After receiving 10 progress updates, Frontend sends: STOP_FITTING (StopFitting)

    file_id = 0
    

Check 3: the FITTING_RESPONSE error should contain “task cancelled”

Step 2: Request fitting again and let it complete

  1. Frontend sends: FITTING_REQUEST (same as step 7)

  2. Backend returns: FITTING_RESPONSE (FittingResponse)

Check 4: the FITTING_RESPONSE should satisfy:

success = true
result_values[0].center = {x: 319.50, y: 399.50}
result_values[0].amp = 10.00
result_values[0].fwhm = {x: 170.64, y: 41.48}
result_values[0].pa = 142.16
log contains "Gaussian fitting with 1 component"
  • All resultErrors should be close to zero