Skip to content
3 changes: 3 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ This file holds the specifications of the camera. This file is must be located t
**camera_rotation_vs_stage_xy**
: In radians, give here the rotation of the position of the rotation axis with respect to the horizontal. Used for diffraction only. Corresponds to the rotation axis in RED and PETS, for example: `-2.24`. You can find the rotation axis for your setup using the script `edtools.find_rotation_axis` available from [here](https://github.com/instamatic-dev/edtools#find_rotation_axispy).

**streamable**
: Boolean value. If present, overwrites the default behavior as implemented in each camera interface class to force the camera to stream (if `True`) or prevent it from streaming (if `False`) all collected data live directly to the GUI.

**stretch_amplitude**
: Use `instamatic.stretch_correction` to characterize the lens distortion. The numbers here are used to calculate the XCORR/YCORR maps. The amplitude is the percentage difference between the maximum and minimum eigenvectors of the ellipsoid, i.e. if the amplitude is `2.43`, eig(max)/eig(min) = 1.0243. You can use the program `instamatic.stretch_correction` available [here](https://github.com/instamatic-dev/instamatic/blob/main/docs/programs.md#instamaticstretch_correction) on some powder patterns to define these numbers.

Expand Down
2 changes: 1 addition & 1 deletion src/instamatic/camera/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations

from .camera import Camera
from .camera import get_camera, get_camera_class
from .videostream import VideoStream
31 changes: 23 additions & 8 deletions src/instamatic/camera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@

import logging
from pathlib import Path
from typing import Optional

from instamatic import config
from instamatic.camera.camera_base import CameraBase

logger = logging.getLogger(__name__)

__all__ = ['Camera']
__all__ = ['get_camera', 'get_camera_class']

default_cam_interface = config.camera.interface


def get_cam(interface: str = None):
"""Grabs the camera object defined by `interface`"""
def get_camera_class(interface: str) -> type[CameraBase]:
"""Grabs the camera class with the specific `interface`"""

simulate = config.settings.simulate

Expand All @@ -39,10 +41,24 @@ def get_cam(interface: str = None):
return cam


def Camera(name: str = None, as_stream: bool = False, use_server: bool = False):
def get_camera(
name: Optional[str] = None,
as_stream: bool = False,
use_server: bool = False,
) -> CameraBase:
"""Initialize the camera identified by the 'name' parameter if `as_stream`
is True, it will return a VideoStream object if `as_stream` is False, it
will return the raw Camera object."""
will return the raw Camera object.

name: Optional[str]
Specify which camera to use, must be implemented in `instamatic.camera`
as_stream: bool
If True (default False), allow streaming this camera image live.
use_server: bool
Connect to camera server running on the host/port defined in the config

returns: Camera interface class instance
"""

if name is None:
name = config.camera.name
Expand All @@ -56,9 +72,8 @@ def Camera(name: str = None, as_stream: bool = False, use_server: bool = False):
from instamatic.camera.camera_client import CamClient

cam = CamClient(name=name, interface=interface)
as_stream = False # precaution
else:
cam_cls = get_cam(interface)
cam_cls = get_camera_class(interface)

if interface in ('timepix', 'pytimepix'):
tpx_config = (
Expand Down Expand Up @@ -221,7 +236,7 @@ def main_entry():

if __name__ == '__main__':
# main_entry()
cam = Camera(use_server=True)
cam = get_camera(use_server=True)
arr = cam.get_image(exposure=0.1)
print(arr)
print(arr.shape)
Expand Down
12 changes: 6 additions & 6 deletions src/instamatic/camera/camera_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ def __init__(
self.name = name
self.interface = interface
self._bufsize = BUFSIZE
self.streamable = False # overrides cam settings
self.verbose = False

try:
Expand Down Expand Up @@ -83,6 +82,7 @@ def __init__(
self.buffers: Dict[str, np.ndarray] = {}
self.shms = {}

self._attr_dct: dict = {}
self._init_dict()
self._init_attr_dict()

Expand All @@ -104,14 +104,14 @@ def connect(self):

def __getattr__(self, attr_name):
if attr_name in self._dct:
if attr_name in object.__getattribute__(self, '_attr_dct'):
return self._eval_dct({'attr_name': attr_name})
wrapped = self._dct[attr_name]
elif attr_name in self._attr_dct:
dct = {'attr_name': attr_name}
return self._eval_dct(dct)
else:
raise AttributeError(
f'`{self.__class__.__name__}` object has no attribute `{attr_name}`'
)
wrapped = None # AFAIK can't wrap with None, can cause odd errors

@wraps(wrapped)
def wrapper(*args, **kwargs):
Expand Down Expand Up @@ -156,9 +156,9 @@ def _eval_dct(self, dct):
def _init_dict(self):
"""Get list of functions and their doc strings from the uninitialized
class."""
from instamatic.camera.camera import get_cam
from instamatic.camera.camera import get_camera_class

cam = get_cam(self.interface)
cam = get_camera_class(self.interface)

self._dct = {
key: value for key, value in cam.__dict__.items() if not key.startswith('_')
Expand Down
6 changes: 3 additions & 3 deletions src/instamatic/camera/videostream.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import numpy as np

from instamatic.camera import Camera
from instamatic.camera import get_camera
from instamatic.camera.camera_base import CameraBase
from instamatic.image_utils import autoscale

Expand Down Expand Up @@ -115,15 +115,15 @@ def from_any(
cls: Type[VideoStream_T], cam: Union[CameraBase, str] = 'simulate'
) -> VideoStream_T:
"""Create a subclass based on passed cam or cam-str stream-ability."""
cam: CameraBase = Camera(name=cam) if isinstance(cam, str) else cam
cam: CameraBase = get_camera(name=cam) if isinstance(cam, str) else cam
if cls is VideoStream:
return (LiveVideoStream if cam.streamable else FakeVideoStream)(cam)
return cls(cam)

def __init__(self, cam: Union[CameraBase, str] = 'simulate') -> None:
threading.Thread.__init__(self)

self.cam: CameraBase = Camera(name=cam) if isinstance(cam, str) else cam
self.cam: CameraBase = get_camera(name=cam) if isinstance(cam, str) else cam
self.lock = threading.Lock()

self.default_exposure = self.cam.default_exposure
Expand Down
4 changes: 2 additions & 2 deletions src/instamatic/config/autoconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ def main():
cam_connect = False
cam_name = None

from instamatic.camera.camera import get_cam
from instamatic.camera.camera import get_camera_class
from instamatic.controller import TEMController
from instamatic.microscope import get_microscope_class

if cam_connect:
cam = get_cam(cam_name)() if cam_name else None
cam = get_camera_class(cam_name)() if cam_name else None
else:
cam = None

Expand Down
4 changes: 2 additions & 2 deletions src/instamatic/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import yaml

from instamatic import config
from instamatic.camera import Camera
from instamatic.camera import get_camera
from instamatic.camera.camera_base import CameraBase
from instamatic.exceptions import TEMControllerError
from instamatic.formats import write_tiff
Expand Down Expand Up @@ -61,7 +61,7 @@ def initialize(

print(f'Camera : {cam_name}{cam_tag}')

cam = Camera(cam_name, as_stream=stream, use_server=use_cam_server)
cam = get_camera(cam_name, as_stream=stream, use_server=use_cam_server)
else:
cam = None

Expand Down
4 changes: 0 additions & 4 deletions src/instamatic/microscope/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,6 @@ def _init_dict(self) -> None:
}
self._dct['get_attrs'] = None

def _init_attr_dict(self):
"""Get list of attrs and their types."""
self._attr_dct = self.get_attrs()

def __dir__(self) -> list:
return list(self._dct.keys())

Expand Down
4 changes: 2 additions & 2 deletions src/instamatic/microscope/microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
__all__ = ['get_microscope', 'get_microscope_class']


def get_microscope_class(interface: str) -> 'type[MicroscopeBase]':
def get_microscope_class(interface: str) -> type[MicroscopeBase]:
"""Grab tem class with the specific 'interface'."""
simulate = config.settings.simulate

Expand Down Expand Up @@ -42,7 +42,7 @@ def get_microscope(name: Optional[str] = None, use_server: bool = False) -> Micr
use_server: bool
Connect to microscope server running on the host/port defined in the config file

returns: TEM interface class
returns: TEM interface class instance
"""
if name is None:
interface = default_tem_interface
Expand Down
4 changes: 2 additions & 2 deletions src/instamatic/server/cam_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import numpy as np

from instamatic import config
from instamatic.camera import Camera
from instamatic.camera import get_camera
from instamatic.utils import high_precision_timers

from .serializer import dumper, loader
Expand Down Expand Up @@ -81,7 +81,7 @@ def copy_data_to_shared_buffer(self, arr):

def run(self):
"""Start server thread."""
self.cam = Camera(name=self._name, use_server=False)
self.cam = get_camera(name=self._name, use_server=False)
self.cam.get_attrs = self.get_attrs

print(f'Initialized camera: {self.cam.interface}')
Expand Down
Loading