About detector/camera control packages

There are 4 packages available for controlling detectors: detector, detectorext, sightx, and femtus.acquisition.
Select the appropriate package based on your system configuration.

../../_images/about_detector_camera_control_pacakge.png

Package summary

Package

Target

Configuration

detector

Cameras and detectors (general)

FEMTUS not configured

detectorext

JEOL cameras

FEMTUS configured

femtus.acquisition

STEM detectors

FEMTUS configured

sightx

Cameras controlled by SightX

SightX configured on the PC

How to use this page

  • For detector, detectorext, and sightx: follow the tutorial steps in Section A below.

  • If FEMTUS is configured and you acquire STEM detector / EDS / EELS images: follow Section B below.

Note

Because FEMTUS does not support this feature, images captured with detectorext are not saved to the FEMTUS worksheet.

Tip

You can verify that each service is reachable in Step 2 of check connection.

Section A: detector, detectorext, sightx

This section describes a standard camera control workflow.

  • For detector, use the sample code as-is.

  • For detectorext and sightx, use the same flow and replace the imported package/module and constructor according to each API.

0. Import package

Import one of the target packages.

  • detector: from PyJEM import detector

  • detectorext: from PyJEM import detectorext

  • sightx: from PyJEM import sightx

  • Offline test example: from PyJEM.offline import detector

1# Example for detector (Ver. 1.3.0)
2from PyJEM import detector
3# from PyJEM import detectorext
4# from PyJEM import sightx
5# from PyJEM.offline import detector  # offline

1. Get usable detector/camera names

Retrieve detector/camera names that can be controlled by this package.

1detector.get_attached_detector()
['HAADF',
 'LAADF',
 'BF',
 'None',
 'SAAF CH1',
 'SAAF CH2',
 'SAAF CH3',
 'SAAF CH4']

2. Get detector/camera information

  1. Set the detector/camera name in detector.Detector("camera/detector").

  2. Get current settings with get_detectorsetting().

1haadf = detector.Detector('HAADF')  # example for HAADF detector; use the appropriate name for your detector
2haadf.get_detectorsetting()
{'AreaModeImagingArea': {'Top': 128, 'Left': 128, 'Width': 256, 'Height': 256},
 'LiveStatus': 'LiveStopped',
 'AreaModeImagingAreaMaximum': '4096, 4096',
 'AreaModeImagingAreaMinimum': '8, 8',
 'CanBinning': False,
 'CanGain': True,
 'CanOffset': True,
 'ExposureTime': [],
 'ExposureTimeIndex': 159,
 'ExposureTimeIndexMaximum': 65533,
 'ExposureTimeIndexMinimum': 31,
 'ExposureTimeString': '3.33',
 'ExposureTimeValue': 3.3333333333333335,
 'frameIntegration': 20,
 'frameIntegrationMaximum': 255,
 'frameIntegrationMinimum': 1,
 'FrameRate': [],
 'GainIndex': 0,
 'GainIndexMaximum': 4095,
 'GainIndexMinimum': 0,
 'HorizontalLineNo': 1,
 'horizontalLineNoMaximum': 512,
 'horizontalLineNoMinimum': 1,
 'ImagingArea': {'X': 0, 'Y': 0, 'Width': 512, 'Height': 512},
 'ImagingAreaMaximum': '4096, 4096',
 'ImagingAreaMinimum': '4, 1',
 'MultiDetectorMode': 2,
 'OffsetIndex': 4095,
 'OffsetIndexMaximum': 4095,
 'OffsetIndexMinimum': 0,
 'OutputImageInformation': {'DataBits': 16,
  'EffectBits': 16,
  'ImageSize': {'Width': 512, 'Height': 512},
  'PixelsPerMeter': {'Horizontal': 2560, 'Vertical': 2560}},
 'scanMode': 0,
 'ScanRotation': 0.0,
 'scanRotationMaximum': 360.0,
 'scanRotationMinimum': 0.0,
 'scanRotationStep': 0.1,
 'SpotPosition': {'X': 0, 'Y': 0},
 'spotPositionMaximum': '4095, 4095',
 'spotPositionMinimum': '0, 0',
 'SyncMode': 0,
 'Version': '1.00',
 'ScanFrameTime': {'FrameTime50Hz': 1337.3066666666668,
  'FrameTime60Hz': 1337.3066666666668,
  'LineTime50Hz': 2.586666666666667,
  'LineTime60Hz': 2.586666666666667}}

3. Change detector/camera setting (example: gain)

You can update detector/camera settings such as gain index.

Note

Available setting functions may differ depending on the package you use. Please check each API document for details: detector, detectorext, sightx.

1haadf.set_gainindex(1234)
{'GainIndex': 1234}

4. Capture image with selected detector/camera

Capture and display an image from the selected detector/camera.

1file=haadf.snapshot("jpg", save=True, show=True)
../../_images/acdc324f4f247d9e8e360684ba465f3d21d85e9411b01911bf0a274e381106d2.png

Section B: femtus.acquisition

This tutorial is based on the femtus.acquisition TODO example and explains each step.

Prerequisites

  • The Scan Area is configured in the FEMTUS UI.

  • Services required for your workflow are running.

  • Use this section when FEMTUS is configured and you acquire STEM / EDS / EELS data.

1# 0) Import packages and confirm connectivity
2from PyJEM.femtus import acquisition

1) Check available detectors

First, confirm which detector names are available for acquisition.

1detectors = acquisition.get_detectors()
2detectors
{'Detectors': ['EDS', 'EELS', 'SAAF', 'Image', '4DSTEM'], 'Version': '1.0.0'}

2) Check acquisition status

Check whether acquisition is already running and inspect current status.

1status = acquisition.get_status()
2status
{'Status': {'AcquisitionID': '00000000-0000-0000-0000-000000000000',
  'AcquisitionRunning': False,
  'SweepCount': 0,
  'ElapsedSeconds': 0},
 'Version': '1.0.0'}

3) Get settings and create a payload

Get current settings from FEMTUS, then edit one setting dictionary and use it as the start() payload.

  • Example below updates detector list, region size, and dwell time.

Note

If no imaging area is selected in the FEMTUS UI, get_settings() cannot retrieve the settings. If nothing is configured, you need to refer to the API page and create the parameters manually.

 1result = acquisition.get_settings()
 2setting = result["Settings"][0]
 3
 4# Example customization
 5setting["Detectors"] = ["Image"]
 6setting["Region"]["Width"] = 128
 7setting["Region"]["Height"] = 128
 8setting["Conditions"]["DwellTime"] = 100
 9
10setting
{'Id': '08274fde-dd82-44c0-906c-d045552c6a43',
 'Detectors': ['Image'],
 'Region': {'Left': 229,
  'Top': 206,
  'Width': 128,
  'Height': 128,
  'Points': None,
  'Shape': 'Rectangle'},
 'Conditions': {'DwellTime': 100,
  'PixelResolution': 1,
  'IsPlayback': False,
  'CollectionMode': {'Mode': 'Sweep', 'Count': 1}},
 'DriftCorrection': {'Type': 'None', 'Interval': 1, 'CorrectionRegion': None}}

4) Start acquisition

Run start(setting) with the payload created above.

  • This call starts acquisition immediately.

  • Execute only when your hardware and scan area are ready.

1start_status = acquisition.start(setting)
2start_status
{'AcquisitionID': 'f0e78d07-01e2-405a-bea7-ea4031fbe0db',
 'WorksheetID': '64f4b900-1c58-41f7-8062-08e24cc41d47',
 'ReferenceDetector': 'VP_ADF_1',
 'Version': '1.0.0'}

4.1) Stop acquisition

After starting acquisition, check status and stop when needed.

1acquisition.stop()
{'Result': 'OK', 'Version': '1.0.0'}

5) Get acquisition result

Use get_acquisition_result to retrieve information for the acquired data.

1acquisition_result = acquisition.get_acquisition_result(start_status.get("AcquisitionID"))
2acquisition_result
{'Results': [{'Id': '050000f2-0944-4e2a-9e00-c5bc4b73642f',
   'DataType': 'STEM_Survey'},
  {'Id': '6f0ffc15-2f69-40ee-bcd0-8f61e4b0ccdb',
   'DataType': 'STEM_HAADF_SeleArea'}]}

6) How to get image information from acquisition results

Use the following steps to get image information from acquisition_result.

  1. Extract the data ID from acquisition_result["Results"][0]["Id"].

  2. Call worksheet.get_data_set(data_id) to retrieve dataset information.

  3. Read Clump and DataBytes in dataset, then proceed to retrieve and decode image bytes.

1from PyJEM.femtus import worksheet
2dataset = worksheet.get_data_set(acquisition_result["Results"][0]["Id"])
3dataset
{'Id': '050000f2-0944-4e2a-9e00-c5bc4b73642f',
 'IsSelected': False,
 'Clump': {'Id': '4456ca54-c67f-4942-8953-311de9fa7af7',
  'Information': {'Header': {'Version': '1.0.0',
    'ClumpId': '4456ca54-c67f-4942-8953-311de9fa7af7',
    'DataType': 'STEM_Survey',
    'DataSubType': '',
    'DetectorType': 2,
    'ClumpType': 'Clump',
    'Name': ''},
   'DataInformation': {'TypeInfo': 0,
    'DataBytes': 2,
    'Channel': 1,
    'DimensionLength': 2,
    'Dimensions': [512, 512],
    'ChannelType': '',
    'IsRangeFixed': False,
    'MinimumPossibleIntensity': -1.7976931348623157e+308,
    'MaximumPossibleIntensity': 1.7976931348623157e+308},
   'MeasurementInformation': {'CalibrationCoefficients': [{'Scale': 14.6875,
      'Offset': 0,
      'Unit': 'Nanometer'},
     {'Scale': 14.697265625, 'Offset': 0, 'Unit': 'Nanometer'}]},
   'Tags': {'General': {'Manufacturer': 'JEOL Ltd.',
     'Comment': '',
     'Specimen': 'Specimen',
     'Operator': 'User',
     'DateTime': '20260402105656758',
     'ProductName': 'FEMTUS',
     'ProductVersion': '2.0.0',
     'UpdateDateTime': '20260402105656758',
     'UpdateProductName': 'FEMTUS',
     'UpdateProductVersion': '2.0.0',
     'AnalysisId': 'f4355252-cdf5-49c8-9858-f1d0fb5761b9',
     'AnalysisDateTime': '20260402164541555',
     'AcquisitionId': 'f0e78d07-01e2-405a-bea7-ea4031fbe0db',
     'AcquisitionDateTime': '20260402164541555',
     'Instrument': 'JEM-ARM200F'},
    'HT': {'GunType': 'Cfeg', 'AccelerationVoltage': 200000, 'EnergyShift': 0},
    'EOS': {'OperationMode': 'Scanning',
     'SpotSizeNumber': 0,
     'ConvergenceAngleAlphaNumber': 0,
     'ImageFormingMode': 'MAG',
     'RockingAngle': 0,
     'MagnificationValue': 20000,
     'MagnificationString': 'x20000',
     'CameraLength': 15,
     'CameraLengthString': '0.015m'},
    'Stage': {'X': {'Position': 0},
     'Y': {'Position': 0},
     'Z': {'Position': 0},
     'TX': {'Position': 0},
     'TY': {'Position': 0},
     'TZ': {'Position': 0},
     'PX': {'Position': 0},
     'PY': {'Position': 0},
     'PZ': {'Position': 0}},
    'ScanGenerator': {'AreaModeImagingArea': {'X': 0,
      'Y': 0,
      'Width': 512,
      'Height': 512},
     'SpotPosition': {'X': 0, 'Y': 0},
     'ScanMode': 'Full',
     'PresetMode': 'Search',
     'ExposureTimeValue': 5,
     'FrameIntegration': 1,
     'ImagingArea': {'X': 0, 'Y': 0, 'Width': 512, 'Height': 512},
     'ScanRotation': 0},
    'Detector': {'DetectorKind': 'HAADF',
     'Manufacturer': 'Manufacture',
     'ModelCode': 'ModelCode',
     'ModelDisplayName': 'DisplayName',
     'GainIndex': 50,
     'OffsetIndex': 50,
     'PixelsPerMeter': {'Horizontal': 3404.255319148936,
      'Vertical': 3401.9933554817276}}}},
  'ViewInformation': {'Header': {'Version': '2.0.0',
    'ClumpId': '4456ca54-c67f-4942-8953-311de9fa7af7'},
   'Tags': {}}}}
1img = worksheet.get_clump_num_array_accessor(dataset["Clump"]["Id"])
2type(img), len(img)
(bytes, 524288)

7) Convert byte data to an image and display

get_clump_num_array_accessor returns raw byte data. In the next cell, convert the bytes to a NumPy array, reshape to a 2D image, and display it with matplotlib.

 1import numpy as np
 2import matplotlib.pyplot as plt
 3
 4# get_clump_num_array_accessor returns raw bytes.
 5# Select dtype from DataBytes when available: 1 -> uint8, 2 -> uint16.
 6data_bytes = dataset["Clump"]["Information"]["DataInformation"]["DataBytes"]
 7size = dataset["Clump"]["Information"]["DataInformation"]["Dimensions"]
 8
 9if data_bytes == 1:
10    dtype = np.uint8
11elif data_bytes == 2:
12    dtype = np.uint16
13else:
14    # Fallback when DataBytes is unavailable in the returned dataset structure.
15    dtype = np.uint16 if (len(img) % 2 == 0) else np.uint8
16
17arr = np.frombuffer(img, dtype=dtype)
18if size[0] * size[1] != arr.size:
19    raise ValueError(f"Cannot infer square image shape from element count: {arr.size}")
20
21img_array = arr.reshape(size[0], size[1])
22plt.imshow(img_array, cmap="gray")
<matplotlib.image.AxesImage at 0x2a5596e24a0>
../../_images/ab621e3ff7e3fabd320373d21dcf335e9c0d689cfc8bb25af2d775eb43c64add.png