Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 49 additions & 10 deletions src/instamatic/camera/camera_gatan2.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
import logging
import sys
import time
import warnings
from pathlib import Path
from typing import Tuple
from typing import Iterator, Optional, Tuple

import numpy as np

from instamatic import config
from instamatic.camera.camera_base import CameraBase
from instamatic.camera.gatansocket3 import GatanSocket
from instamatic.utils.deprecated import VisibleDeprecationWarning

logger = logging.getLogger(__name__)

Expand All @@ -32,7 +34,6 @@ def __init__(self, name: str = 'gatan2'):
self.load_defaults()

msg = f'Camera `{self.get_camera_name()}` ({self.name}) initialized'
# print(msg)
logger.info(msg)

atexit.register(self.release_connection)
Expand Down Expand Up @@ -75,33 +76,71 @@ def write_tiffs(self) -> None:

def get_image(
self,
exposure=0.400,
binning=1,
processing='gain normalized',
exposure: float | None = None,
binsize: int | None = None,
*,
binning: int | None = None,
processing: str = 'gain normalized',
**kwargs,
) -> 'np.array':
"""Acquire image through DM and return data as np array."""
"""Acquire image through DM and return data as np array.

Notes
-----
Instamatic's public API uses `binsize` everywhere (controller,
videostream, CLI). Historically this driver used `binning` instead,
which prevented GUI/Controller code from changing binning.
This implementation accepts both names. `binsize`, `binning`,
and `default_binsize` take precedence in this order.
Use of `binning` is deprecated and might be removed in the future.
"""

exposure = exposure if exposure is not None else self.default_exposure

if binning is not None:
msg = 'Argument `binning` is deprecated since v2.3.0. Use `binsize` instead.'
warnings.warn(msg, VisibleDeprecationWarning, stacklevel=2)

if binsize is None:
binsize = binning if binning is not None else self.default_binsize

width, height = self.dimensions
if (p := getattr(self, 'possible_binsizes', [])) and binsize not in p:
msg = f'Cannot use {binsize=}, should be one of {self.possible_binsizes=}'
raise ValueError(msg)

width, height = [int(wh) for wh in self.dimensions][:2]
top = 0
left = 0
bottom = height
right = width
bottom = int(height)
right = int(width)

arr = self.g.get_image(
processing=processing,
height=height,
width=width,
binning=binning,
binning=binsize,
top=top,
left=left,
bottom=bottom,
right=right,
exposure=exposure,
shutterDelay=0,
**kwargs,
)

return arr

def get_movie(self, n_frames: int, *args, **kwargs) -> Iterator[np.ndarray]:
"""Yield `n_frames` images by repeatedly calling `get_image`.

The SerialEMCCD socket protocol seems to have no native streaming call,
so each frame here is a full round trip: re-arm, expose, read. There is
no guarantee of even dead time or frame spacing - this is a convenience
wrapper only. Provide a better implementation if you can.
"""
for _ in range(n_frames):
yield self.get_image(*args, **kwargs)

def acquire_image(self, **kwargs) -> 'np.array':
"""Acquire image through DM."""
return self.get_image(**kwargs)
Expand Down
Loading
Loading