def detectorbase_start(self):
     from iotbx.detectors import SMVImage
     self.detectorbase = SMVImage(self._image_file)
     self.detectorbase.open_file = self.open_file
     self.detectorbase.readHeader()
     self.detectorbase.parameters['SIZE1'] = 2527
     self.detectorbase.parameters['SIZE2'] = 2463
    def detectorbase_start(self):
        if not hasattr(self, "detectorbase") or self.detectorbase is None:
            from iotbx.detectors import SMVImage

            self.detectorbase = SMVImage(self._image_file)
            self.detectorbase.open_file = self.open_file
            self.detectorbase.readHeader()
Beispiel #3
0
class FormatSMVADSCDBG(FormatSMVADSC):
    """ Format class for reading images converted from pilatus to smv adsc"""
    @staticmethod
    def understand(image_file):
        """Check to see if this has the correct dimensions of a pilatus"""

        size, header = FormatSMVADSC.get_smv_header(image_file)

        if int(header["SIZE1"]) == 2463 and int(header["SIZE2"]) == 2527:
            return True

        return False

    def _start(self):
        # read the headers, then swap size1 and size2
        FormatSMVADSC._start(self)
        self._header_dictionary["SIZE1"] = "2527"
        self._header_dictionary["SIZE2"] = "2463"

    def detectorbase_start(self):
        from iotbx.detectors import SMVImage

        self.detectorbase = SMVImage(self._image_file)
        self.detectorbase.open_file = self.open_file
        self.detectorbase.readHeader()
        self.detectorbase.parameters["SIZE1"] = 2527
        self.detectorbase.parameters["SIZE2"] = 2463

    def _detector(self):
        """Return a model for a simple detector, presuming no one has
        one of these on a two-theta stage. Assert that the beam centre is
        provided in the Mosflm coordinate frame."""

        distance = float(self._header_dictionary["DISTANCE"])
        beam_x = float(self._header_dictionary["BEAM_CENTER_X"])
        beam_y = float(self._header_dictionary["BEAM_CENTER_Y"])
        pixel_size = float(self._header_dictionary["PIXEL_SIZE"])
        # size1 and size2 swapped here
        image_size = (
            float(self._header_dictionary["SIZE2"]),
            float(self._header_dictionary["SIZE1"]),
        )
        overload = 65535
        underload = 0

        return self._detector_factory.simple(
            "CCD",
            distance,
            (beam_y, beam_x),
            "+x",
            "-y",
            (pixel_size, pixel_size),
            image_size,
            (underload, overload),
            [],
        )
class FormatSMVADSCDBG(FormatSMVADSC):
    """ Format class for reading images converted from pilatus to smv adsc"""

    @staticmethod
    def understand(image_file):
        """Check to see if this has the correct dimensions of a pilatus"""

        size, header = FormatSMVADSC.get_smv_header(image_file)

        if int(header["SIZE1"]) == 2463 and int(header["SIZE2"]) == 2527:
            return True

        return False

    def _start(self):
        # read the headers, then swap size1 and size2
        FormatSMVADSC._start(self)
        self._header_dictionary["SIZE1"] = "2527"
        self._header_dictionary["SIZE2"] = "2463"

        from iotbx.detectors import SMVImage

        self.detectorbase = SMVImage(self._image_file)
        self.detectorbase.readHeader()
        self.detectorbase.parameters["SIZE1"] = 2527
        self.detectorbase.parameters["SIZE2"] = 2463

    def _detector(self):
        """Return a model for a simple detector, presuming no one has
    one of these on a two-theta stage. Assert that the beam centre is
    provided in the Mosflm coordinate frame."""

        distance = float(self._header_dictionary["DISTANCE"])
        beam_x = float(self._header_dictionary["BEAM_CENTER_X"])
        beam_y = float(self._header_dictionary["BEAM_CENTER_Y"])
        pixel_size = float(self._header_dictionary["PIXEL_SIZE"])
        # size1 and size2 swapped here
        image_size = (float(self._header_dictionary["SIZE2"]), float(self._header_dictionary["SIZE1"]))
        overload = 65535
        underload = 0

        return self._detector_factory.simple(
            "CCD",
            distance,
            (beam_y, beam_x),
            "+x",
            "-y",
            (pixel_size, pixel_size),
            image_size,
            (underload, overload),
            [],
        )
class FormatSMVADSCDBG(FormatSMVADSC):
    ''' Format class for reading images converted from pilatus to smv adsc'''
    @staticmethod
    def understand(image_file):
        '''Check to see if this has the correct dimensions of a pilatus'''

        size, header = FormatSMVADSC.get_smv_header(image_file)

        if int(header['SIZE1']) == 2463 and int(header['SIZE2']) == 2527:
            return True

        return False

    def _start(self):
        # read the headers, then swap size1 and size2
        FormatSMVADSC._start(self)
        self._header_dictionary['SIZE1'] = '2527'
        self._header_dictionary['SIZE2'] = '2463'

    def detectorbase_start(self):
        from iotbx.detectors import SMVImage
        self.detectorbase = SMVImage(self._image_file)
        self.detectorbase.open_file = self.open_file
        self.detectorbase.readHeader()
        self.detectorbase.parameters['SIZE1'] = 2527
        self.detectorbase.parameters['SIZE2'] = 2463

    def _detector(self):
        '''Return a model for a simple detector, presuming no one has
    one of these on a two-theta stage. Assert that the beam centre is
    provided in the Mosflm coordinate frame.'''

        distance = float(self._header_dictionary['DISTANCE'])
        beam_x = float(self._header_dictionary['BEAM_CENTER_X'])
        beam_y = float(self._header_dictionary['BEAM_CENTER_Y'])
        pixel_size = float(self._header_dictionary['PIXEL_SIZE'])
        # size1 and size2 swapped here
        image_size = (float(self._header_dictionary['SIZE2']),
                      float(self._header_dictionary['SIZE1']))
        overload = 65535
        underload = 0

        return self._detector_factory.simple('CCD', distance, (beam_y, beam_x),
                                             '+x', '-y',
                                             (pixel_size, pixel_size),
                                             image_size, (underload, overload),
                                             [])
Beispiel #6
0
  def _start(self):

    FormatSMV._start(self)

    if not hasattr(self, "detectorbase") or self.detectorbase is None:
      from iotbx.detectors import SMVImage
      self.detectorbase = SMVImage(self._image_file)
      self.detectorbase.readHeader()
class FormatSMVADSCDBG(FormatSMVADSC):
  ''' Format class for reading images converted from pilatus to smv adsc'''

  @staticmethod
  def understand(image_file):
    '''Check to see if this has the correct dimensions of a pilatus'''

    size, header = FormatSMVADSC.get_smv_header(image_file)

    if int(header['SIZE1']) == 2463 and int(header['SIZE2']) == 2527:
      return True

    return False

  def _start(self):
    # read the headers, then swap size1 and size2
    FormatSMVADSC._start(self)
    self._header_dictionary['SIZE1'] = '2527'
    self._header_dictionary['SIZE2'] = '2463'

  def detectorbase_start(self):
    from iotbx.detectors import SMVImage
    self.detectorbase = SMVImage(self._image_file)
    self.detectorbase.readHeader()
    self.detectorbase.parameters['SIZE1'] = 2527
    self.detectorbase.parameters['SIZE2'] = 2463

  def _detector(self):
    '''Return a model for a simple detector, presuming no one has
    one of these on a two-theta stage. Assert that the beam centre is
    provided in the Mosflm coordinate frame.'''

    distance = float(self._header_dictionary['DISTANCE'])
    beam_x = float(self._header_dictionary['BEAM_CENTER_X'])
    beam_y = float(self._header_dictionary['BEAM_CENTER_Y'])
    pixel_size = float(self._header_dictionary['PIXEL_SIZE'])
    # size1 and size2 swapped here
    image_size = (float(self._header_dictionary['SIZE2']),
                  float(self._header_dictionary['SIZE1']))
    overload = 65535
    underload = 0

    return self._detector_factory.simple(
        'CCD', distance, (beam_y, beam_x), '+x', '-y',
        (pixel_size, pixel_size), image_size, (underload, overload), [])
    def _start(self):
        # read the headers, then swap size1 and size2
        FormatSMVADSC._start(self)
        self._header_dictionary["SIZE1"] = "2527"
        self._header_dictionary["SIZE2"] = "2463"

        from iotbx.detectors import SMVImage

        self.detectorbase = SMVImage(self._image_file)
        self.detectorbase.readHeader()
        self.detectorbase.parameters["SIZE1"] = 2527
        self.detectorbase.parameters["SIZE2"] = 2463
Beispiel #9
0
 def detectorbase_start(self):
     self.detectorbase = SMVImage(self._image_file)
     self.detectorbase.open_file = self.open_file
     self.detectorbase.readHeader()
     self.detectorbase.parameters["SIZE1"] = 2527
     self.detectorbase.parameters["SIZE2"] = 2463
class FormatSMVTimePix_SU(FormatSMV):
    '''Base format class to specifically recognise images from a Timepix-based
  detector, installed on a JEOL-2100 electron microscope at Stockholm
  University, which have been preprocessed by Wei Wan's RED software,
  to produce SMV files. See https://doi.org/10.1107/S0021889813027714 for RED.
  The detector itself has a 2x2 array of Timepix modules. Derived classes will
  either treat these separately as a multi-panel model, or as a single panel
  model.'''
    @staticmethod
    def understand(image_file):

        size, header = FormatSMV.get_smv_header(image_file)

        # only recognise TimePix_SU
        if header.get('BEAMLINE', '').upper() != 'TIMEPIX_SU': return False

        # check the header contains the things we're going to use
        wanted_header_items = [
            'BEAM_CENTER_X', 'BEAM_CENTER_Y', 'DISTANCE', 'WAVELENGTH',
            'PIXEL_SIZE', 'OSC_START', 'OSC_RANGE', 'PHI', 'SIZE1', 'SIZE2',
            'BYTE_ORDER', 'DETECTOR_SN'
        ]
        for header_item in wanted_header_items:
            if not header_item in header:
                return False

        return True

    def __init__(self, image_file, **kwargs):
        '''Initialise the image structure from the given file, including a
    proper model of the experiment.'''

        from dxtbx import IncorrectFormatError
        if not self.understand(image_file):
            raise IncorrectFormatError(self, image_file)

        FormatSMV.__init__(self, image_file, **kwargs)

    def _start(self):

        FormatSMV._start(self)

    def detectorbase_start(self):
        if not hasattr(self, "detectorbase") or self.detectorbase is None:
            from iotbx.detectors import SMVImage
            self.detectorbase = SMVImage(self._image_file)
            self.detectorbase.open_file = self.open_file
            self.detectorbase.readHeader()

    def _goniometer(self):
        '''Return a model for a simple single-axis goniometer. For this beamline
    this should be close to the provided values and neither aligned with the
    detector slow or fast axes.'''

        return self._goniometer_factory.known_axis((-0.755, -0.656, 0.0))

    def _beam(self):
        '''Return an unpolarized beam model.'''

        wavelength = float(self._header_dictionary['WAVELENGTH'])

        return self._beam_factory.make_polarized_beam(
            sample_to_source=(0.0, 0.0, 1.0),
            wavelength=wavelength,
            polarization=(0, 1, 0),
            polarization_fraction=0.5)

    def _scan(self):
        '''Return the scan information for this image.'''
        import calendar

        format = self._scan_factory.format('SMV')
        exposure_time = float(self._header_dictionary['TIME'])
        epoch = None

        # PST, PDT timezones not recognised by default...

        epoch = 0
        try:
            date_str = self._header_dictionary['DATE']
            date_str = date_str.replace('PST', '').replace('PDT', '')
        except KeyError:
            date_str = ''
        for format_string in [
                '%a %b %d %H:%M:%S %Y', '%a %b %d %H:%M:%S %Z %Y'
        ]:
            try:
                epoch = calendar.timegm(time.strptime(date_str, format_string))
                break
            except ValueError:
                pass

        # assert(epoch)
        osc_start = float(self._header_dictionary['OSC_START'])
        osc_range = float(self._header_dictionary['OSC_RANGE'])

        return self._scan_factory.single(self._image_file, format,
                                         exposure_time, osc_start, osc_range,
                                         epoch)
Beispiel #11
0
class FormatSMVJHSim(FormatSMV):
    '''A class for reading SMV format JHSim images, and correctly constructing
  a model for the experiment from this.'''

    # all ADSC detectors generate images with an ADC offset of 40
    # for Mar/Rayonix it is 10
    # Rigaku SMV uses 20, and 5 for image plate formats
    # for one particular simulation, I used 1
    ADC_OFFSET = 1
    image_pedestal = 1

    @staticmethod
    def understand(image_file):
        '''Check to see if this looks like an JHSim SMV format image, i.e. we can
    make sense of it. From JH: "The best way to identify images from any of my
    simulators is to look for BEAMLINE=fake in the header."'''

        size, header = FormatSMV.get_smv_header(image_file)

        if header.get('BEAMLINE') == 'fake':
            return True
        else:
            return False

    def __init__(self, image_file, **kwargs):
        '''Initialise the image structure from the given file, including a
    proper model of the experiment.'''

        from dxtbx import IncorrectFormatError
        if not self.understand(image_file):
            raise IncorrectFormatError(self, image_file)

        FormatSMV.__init__(self, image_file, **kwargs)

        return

    def _start(self):

        FormatSMV._start(self)

    def detectorbase_start(self):
        if not hasattr(self, "detectorbase") or self.detectorbase is None:
            from iotbx.detectors import SMVImage
            self.detectorbase = SMVImage(self._image_file)
            self.detectorbase.open_file = self.open_file
            self.detectorbase.readHeader()

    def _goniometer(self):
        '''Return a model for a simple single-axis goniometer. This should
    probably be checked against the image header.'''

        return self._goniometer_factory.single_axis()

    def _detector(self):
        '''Return a model for a simple detector, presuming no one has
    one of these on a two-theta stage. Assert that the beam centre is
    provided in the Mosflm coordinate frame.'''

        distance = float(self._header_dictionary['DISTANCE'])
        beam_x = float(self._header_dictionary['BEAM_CENTER_X'])
        beam_y = float(self._header_dictionary['BEAM_CENTER_Y'])
        pixel_size = float(self._header_dictionary['PIXEL_SIZE'])
        image_size = (float(self._header_dictionary['SIZE1']),
                      float(self._header_dictionary['SIZE2']))
        image_pedestal = 1
        try:
            image_pedestal = float(self._header_dictionary['ADC_OFFSET'])
        except (KeyError):
            pass
        overload = 65535 - image_pedestal
        underload = 1 - image_pedestal

        # interpret beam center conventions
        image_width_mm = pixel_size * image_size[0]
        image_height_mm = pixel_size * image_size[1]
        adxv_beam_center = (beam_x, beam_y)
        mosflm_beam_center = (image_height_mm - adxv_beam_center[1],
                              adxv_beam_center[0])
        xds_beam_center = (adxv_beam_center[0] / pixel_size,
                           (image_height_mm - adxv_beam_center[1]) /
                           pixel_size)
        cctbx_beam_center = (adxv_beam_center[0] + pixel_size,
                             image_height_mm - adxv_beam_center[1] +
                             pixel_size)

        # Guess whether this is mimicking a Pilatus, if so set detector type so
        # that spot-finding parameters are appropriate
        if pixel_size == 0.172:
            stype = 'SENSOR_PAD'
        else:
            stype = 'CCD'

        return self._detector_factory.simple(stype, distance,
                                             cctbx_beam_center, '+x', '-y',
                                             (pixel_size, pixel_size),
                                             image_size, (underload, overload),
                                             [])

    def _beam(self):
        '''Return a simple model for the beam.'''

        wavelength = float(self._header_dictionary['WAVELENGTH'])

        return self._beam_factory.simple(wavelength)

    def _scan(self):
        '''Return the scan information for this image.'''
        import calendar

        format = self._scan_factory.format('SMV')
        exposure_time = 1
        epoch = None

        # PST, PDT timezones not recognised by default...

        epoch = 0
        try:
            date_str = self._header_dictionary['DATE']
            date_str = date_str.replace('PST', '').replace('PDT', '')
        except KeyError, e:
            date_str = ''
        for format_string in [
                '%a %b %d %H:%M:%S %Y', '%a %b %d %H:%M:%S %Z %Y'
        ]:
            try:
                epoch = calendar.timegm(time.strptime(date_str, format_string))
                break
            except ValueError, e:
                pass
Beispiel #12
0
class FormatSMVTimePix_SU(FormatSMV):
    """Base format class to specifically recognise images from a Timepix-based
    detector, installed on a JEOL-2100 electron microscope at Stockholm
    University, which have been preprocessed by Wei Wan's RED software,
    to produce SMV files. See https://doi.org/10.1107/S0021889813027714 for RED.
    The detector itself has a 2x2 array of Timepix modules. Derived classes will
    either treat these separately as a multi-panel model, or as a single panel
    model."""
    @staticmethod
    def understand(image_file):

        size, header = FormatSMV.get_smv_header(image_file)

        # only recognise TimePix_SU
        if header.get("BEAMLINE", "").upper() != "TIMEPIX_SU":
            return False

        # check the header contains the things we're going to use
        wanted_header_items = [
            "BEAM_CENTER_X",
            "BEAM_CENTER_Y",
            "DISTANCE",
            "WAVELENGTH",
            "PIXEL_SIZE",
            "OSC_START",
            "OSC_RANGE",
            "PHI",
            "SIZE1",
            "SIZE2",
            "BYTE_ORDER",
            "DETECTOR_SN",
        ]
        if any(item not in header for item in wanted_header_items):
            return False

        return True

    def detectorbase_start(self):
        if not hasattr(self, "detectorbase") or self.detectorbase is None:
            self.detectorbase = SMVImage(self._image_file)
            self.detectorbase.open_file = self.open_file
            self.detectorbase.readHeader()

    def _goniometer(self):
        """Return a model for a simple single-axis goniometer. For this beamline
        this should be close to the provided values and neither aligned with the
        detector slow or fast axes."""

        return self._goniometer_factory.known_axis((-0.755, -0.656, 0.0))

    def _beam(self):
        """Return an unpolarized beam model."""

        wavelength = float(self._header_dictionary["WAVELENGTH"])

        return self._beam_factory.make_polarized_beam(
            sample_to_source=(0.0, 0.0, 1.0),
            wavelength=wavelength,
            polarization=(0, 1, 0),
            polarization_fraction=0.5,
        )

    def _scan(self):
        """Return the scan information for this image."""
        format = self._scan_factory.format("SMV")
        exposure_time = float(self._header_dictionary["TIME"])
        epoch = None

        # PST, PDT timezones not recognised by default...

        epoch = 0
        try:
            date_str = self._header_dictionary["DATE"]
            date_str = date_str.replace("PST", "").replace("PDT", "")
        except KeyError:
            date_str = ""
        for format_string in [
                "%a %b %d %H:%M:%S %Y", "%a %b %d %H:%M:%S %Z %Y"
        ]:
            try:
                epoch = calendar.timegm(time.strptime(date_str, format_string))
                break
            except ValueError:
                pass

        # assert(epoch)
        osc_start = float(self._header_dictionary["OSC_START"])
        osc_range = float(self._header_dictionary["OSC_RANGE"])

        return self._scan_factory.single(self._image_file, format,
                                         exposure_time, osc_start, osc_range,
                                         epoch)
Beispiel #13
0
class FormatSMVADSC(FormatSMV):
    """A class for reading SMV format ADSC images, and correctly constructing
    a model for the experiment from this."""
    @staticmethod
    def understand(image_file):
        """Check to see if this looks like an ADSC SMV format image, i.e. we
        can make sense of it. Essentially that will be if it contains all of
        the keys we are looking for and not some we are not (i.e. that belong
        to a Rigaku Saturn.)"""

        size, header = FormatSMV.get_smv_header(image_file)

        # do not understand JHSim images
        if header.get("BEAMLINE") == "fake":
            return False

        # do not understand Timepix_SU images
        if header.get("BEAMLINE", "").upper() == "TIMEPIX_SU":
            return False

        # this used to include TIME
        wanted_header_items = [
            "BEAM_CENTER_X",
            "BEAM_CENTER_Y",
            "DISTANCE",
            "WAVELENGTH",
            "PIXEL_SIZE",
            "OSC_START",
            "OSC_RANGE",
            "SIZE1",
            "SIZE2",
            "BYTE_ORDER",
        ]

        if any(item not in header for item in wanted_header_items):
            return False

        unwanted_header_items = ["DTREK_DATE_TIME"]

        if any(item in header for item in unwanted_header_items):
            return False

        return True

    def detectorbase_start(self):
        if not hasattr(self, "detectorbase") or self.detectorbase is None:
            self.detectorbase = SMVImage(self._image_file)
            self.detectorbase.open_file = self.open_file
            self.detectorbase.readHeader()

    def _goniometer(self):
        """Return a model for a simple single-axis goniometer. Invert the axis if
        the rotation range is negative."""

        if float(self._header_dictionary["OSC_RANGE"]) > 0:
            return self._goniometer_factory.single_axis()
        else:
            return self._goniometer_factory.single_axis_reverse()

    def _adsc_trusted_range(self, pedestal=None):
        """Return a 16 bit trusted range shifted to account for any image
        pedestal that is present"""

        if pedestal is None:
            pedestal = int(self._header_dictionary.get("IMAGE_PEDESTAL", 0))

        overload = 65535 - pedestal
        underload = -1 - pedestal

        return underload, overload

    def _adsc_module_gain(self, model=None):
        """Return an appropriate gain value in ADU per captured X-ray for an
        ADSC CCD module. If the model is None (unknown) then make a guess based
        on header details"""

        # Values are based on a combination of manufacturer datasheets,
        # James Holton's list at http://bl831.als.lbl.gov/xtalsize.html and
        # empirical guesswork based on spot finding results. The accuracy should
        # not be expected to be better than 20% and indeed may be worse than that.
        # Values may refer to gain in ADU per incident photon rather than the
        # more appropriate ADU per captured photon.

        # Get the binning level
        bin_lev = str(self._header_dictionary.get("BIN"))

        if model is None:
            # guesswork based on the header details
            npx1 = int(self._header_dictionary["SIZE1"])
            npx2 = int(self._header_dictionary["SIZE2"])
            px_sz = float(self._header_dictionary["PIXEL_SIZE"])
            if npx1 == npx2 == 2304 and px_sz == 0.0816:
                model = "Q4"  # or Q4R
            elif npx1 == npx2 == 4096 and px_sz == 0.0512:
                model = "Q210"  # or Q210R bin none or 1x1
            elif npx1 == npx2 == 2048 and px_sz == 0.1024:
                model = "Q210"  # or Q210R bin 2x2
                bin_lev = "2x2"
            elif npx1 == npx2 == 6144 and px_sz == 0.0512:
                model = "Q315"  # or Q315R bin none or 1x1
            elif npx1 == npx2 == 3072 and px_sz == 0.1024:
                model = "Q315"  # or Q315R bin 2x2
                bin_lev = "2x2"
            elif npx1 == npx2 == 4168 and px_sz == 0.0648:
                model = "Q270"  # or Q270R bin none or 1x1
            elif npx1 == npx2 == 2084 and px_sz == 0.0324:
                model = "Q270"  # or Q270R bin 2x2
                bin_lev = "2x2"
            else:
                # Unidentified model
                return 1.0

        model = model.upper()
        if model.startswith("Q270"):
            # Don't know the difference between binning modes in this case
            return 2.8

        if model.startswith("Q4"):
            # https://web.archive.org/web/20051224172252/http://www.adsc-xray.com:80/prod_compare.html
            # Don't know the difference between binning modes in this case
            return 1.25

        # Get binning mode HW/SW
        bin_type = self._header_dictionary.get("BIN_TYPE")

        if bin_lev.upper() == "NONE":
            bin_type = None
        elif bin_type != "HW":
            bin_type = "SW"

        if bin_type == "SW":
            # return 0.6 This does not look believable. Default to 2.4 instead
            return 2.4

        if bin_type != "HW":  # assume unbinned
            return 2.4
        elif model.endswith("R"):  # e.g. Q315r, HW binning
            return 1.8
        else:  # e.g. Q315, HW binning
            return 2.4

    def _detector(self):
        """Return a model for a simple detector, presuming no one has
        one of these on a two-theta stage. Assert that the beam centre is
        provided in the Mosflm coordinate frame."""

        distance = float(self._header_dictionary["DISTANCE"])
        beam_x = float(self._header_dictionary["BEAM_CENTER_X"])
        beam_y = float(self._header_dictionary["BEAM_CENTER_Y"])
        pixel_size = float(self._header_dictionary["PIXEL_SIZE"])
        image_size = (
            float(self._header_dictionary["SIZE1"]),
            float(self._header_dictionary["SIZE2"]),
        )

        return self._detector_factory.simple(
            "CCD",
            distance,
            (beam_y, beam_x),
            "+x",
            "-y",
            (pixel_size, pixel_size),
            image_size,
            self._adsc_trusted_range(),
            [],
            gain=self._adsc_module_gain(),
            pedestal=int(self._header_dictionary.get("IMAGE_PEDESTAL", 0)),
        )

    def _beam(self):
        """Return a simple model for the beam."""

        wavelength = float(self._header_dictionary["WAVELENGTH"])

        return self._beam_factory.simple(wavelength)

    def _scan(self):
        """Return the scan information for this image."""
        format = self._scan_factory.format("SMV")
        exposure_time = float(self._header_dictionary["TIME"])
        epoch = None

        # PST, PDT timezones not recognised by default...

        epoch = 0
        try:
            date_str = self._header_dictionary["DATE"]
            date_str = date_str.replace("PST", "").replace("PDT", "")
        except KeyError:
            date_str = ""
        for format_string in [
                "%a %b %d %H:%M:%S %Y", "%a %b %d %H:%M:%S %Z %Y"
        ]:
            try:
                epoch = calendar.timegm(time.strptime(date_str, format_string))
                break
            except ValueError:
                pass

        # assert(epoch)
        osc_start = float(self._header_dictionary["OSC_START"])
        osc_range = abs(float(self._header_dictionary["OSC_RANGE"]))

        return self._scan_factory.single(self._image_file, format,
                                         exposure_time, osc_start, osc_range,
                                         epoch)

    def get_raw_data(self):
        """Get the pixel intensities (i.e. read the image and return as a
        flex array of integers.)"""

        assert len(self.get_detector()) == 1
        panel = self.get_detector()[0]
        image_size = panel.get_image_size()
        return self._get_endianic_raw_data(size=image_size)
class FormatSMVJHSim(FormatSMV):
    """A class for reading SMV format JHSim images, and correctly constructing
    a model for the experiment from this."""

    # all ADSC detectors generate images with an ADC offset of 40
    # for Mar/Rayonix it is 10
    # Rigaku SMV uses 20, and 5 for image plate formats
    # for one particular simulation, I used 1
    ADC_OFFSET = 1
    image_pedestal = 1

    @staticmethod
    def understand(image_file):
        """Check to see if this looks like an JHSim SMV format image, i.e. we can
        make sense of it. From JH: "The best way to identify images from any of my
        simulators is to look for BEAMLINE=fake in the header."."""

        size, header = FormatSMV.get_smv_header(image_file)

        if header.get("BEAMLINE") == "fake":
            return True
        else:
            return False

    def __init__(self, image_file, **kwargs):
        """Initialise the image structure from the given file, including a
        proper model of the experiment."""

        from dxtbx import IncorrectFormatError

        if not self.understand(image_file):
            raise IncorrectFormatError(self, image_file)

        FormatSMV.__init__(self, image_file, **kwargs)

        return

    def _start(self):

        FormatSMV._start(self)

    def detectorbase_start(self):
        if not hasattr(self, "detectorbase") or self.detectorbase is None:
            from iotbx.detectors import SMVImage

            self.detectorbase = SMVImage(self._image_file)
            self.detectorbase.open_file = self.open_file
            self.detectorbase.readHeader()

    def _goniometer(self):
        """Return a model for a simple single-axis goniometer. This should
        probably be checked against the image header."""

        return self._goniometer_factory.single_axis()

    def _detector(self):
        """Return a model for a simple detector, presuming no one has
        one of these on a two-theta stage. Assert that the beam centre is
        provided in the Mosflm coordinate frame."""

        distance = float(self._header_dictionary["DISTANCE"])
        beam_x = float(self._header_dictionary["BEAM_CENTER_X"])
        beam_y = float(self._header_dictionary["BEAM_CENTER_Y"])
        pixel_size = float(self._header_dictionary["PIXEL_SIZE"])
        image_size = (
            float(self._header_dictionary["SIZE1"]),
            float(self._header_dictionary["SIZE2"]),
        )
        image_pedestal = 1
        try:
            image_pedestal = float(self._header_dictionary["ADC_OFFSET"])
        except (KeyError):
            pass
        overload = 65535 - image_pedestal
        underload = 1 - image_pedestal

        # interpret beam center conventions
        image_height_mm = pixel_size * image_size[1]
        adxv_beam_center = (beam_x, beam_y)
        cctbx_beam_center = (
            adxv_beam_center[0] + pixel_size,
            image_height_mm - adxv_beam_center[1] + pixel_size,
        )

        # Guess whether this is mimicking a Pilatus, if so set detector type so
        # that spot-finding parameters are appropriate
        if pixel_size == 0.172:
            stype = "SENSOR_PAD"
        else:
            stype = "CCD"

        return self._detector_factory.simple(
            stype,
            distance,
            cctbx_beam_center,
            "+x",
            "-y",
            (pixel_size, pixel_size),
            image_size,
            (underload, overload),
            [],
        )

    def _beam(self):
        """Return a simple model for the beam."""

        wavelength = float(self._header_dictionary["WAVELENGTH"])

        return self._beam_factory.simple(wavelength)

    def _scan(self):
        """Return the scan information for this image."""
        import calendar

        format = self._scan_factory.format("SMV")
        exposure_time = 1
        epoch = None

        # PST, PDT timezones not recognised by default...

        epoch = 0
        try:
            date_str = self._header_dictionary["DATE"]
            date_str = date_str.replace("PST", "").replace("PDT", "")
        except KeyError:
            date_str = ""
        for format_string in [
                "%a %b %d %H:%M:%S %Y", "%a %b %d %H:%M:%S %Z %Y"
        ]:
            try:
                epoch = calendar.timegm(time.strptime(date_str, format_string))
                break
            except ValueError:
                pass

        # assert(epoch)
        osc_start = float(self._header_dictionary["OSC_START"])
        osc_range = float(self._header_dictionary["OSC_RANGE"])

        return self._scan_factory.single(self._image_file, format,
                                         exposure_time, osc_start, osc_range,
                                         epoch)

    def get_raw_data(self):
        """Get the pixel intensities (i.e. read the image and return as a
        flex array of integers.)"""

        assert len(self.get_detector()) == 1
        panel = self.get_detector()[0]
        image_size = panel.get_image_size()
        raw_data = self._get_endianic_raw_data(size=image_size)

        # apply image pedestal, will result in *negative pixel values*
        # this is a horrible idea since raw_data is unsigned
        # see all instances of image_pedestal in dxtbx
        image_pedestal = self._header_dictionary.get("ADC_OFFSET", 1)
        raw_data -= int(image_pedestal)

        return raw_data
 def detectorbase_start(self):
   from iotbx.detectors import SMVImage
   self.detectorbase = SMVImage(self._image_file)
   self.detectorbase.readHeader()
   self.detectorbase.parameters['SIZE1'] = 2527
   self.detectorbase.parameters['SIZE2'] = 2463
Beispiel #16
0
class FormatSMVADSC(FormatSMV):
  '''A class for reading SMV format ADSC images, and correctly constructing
  a model for the experiment from this.'''

  @staticmethod
  def understand(image_file):
    '''Check to see if this looks like an ADSC SMV format image, i.e. we
    can make sense of it. Essentially that will be if it contains all of
    the keys we are looking for and not some we are not (i.e. that belong
    to a Rigaku Saturn.)'''

    size, header = FormatSMV.get_smv_header(image_file)

    wanted_header_items = ['BEAM_CENTER_X', 'BEAM_CENTER_Y',
                           'DISTANCE', 'WAVELENGTH', 'PIXEL_SIZE',
                           'OSC_START', 'OSC_RANGE', 'SIZE1', 'SIZE2',
                           'BYTE_ORDER', 'TIME']

    for header_item in wanted_header_items:
      if not header_item in header:
        return 0

    unwanted_header_items = ['DTREK_DATE_TIME']

    for header_item in unwanted_header_items:
      if header_item in header:
        return False

    return True

  def __init__(self, image_file, **kwargs):
    '''Initialise the image structure from the given file, including a
    proper model of the experiment.'''

    assert(self.understand(image_file))

    FormatSMV.__init__(self, image_file, **kwargs)

    return

  def _start(self):

    FormatSMV._start(self)

  def detectorbase_start(self):
    if not hasattr(self, "detectorbase") or self.detectorbase is None:
      from iotbx.detectors import SMVImage
      self.detectorbase = SMVImage(self._image_file)
      self.detectorbase.readHeader()

  def _goniometer(self):
    '''Return a model for a simple single-axis goniometer. This should
    probably be checked against the image header.'''

    return self._goniometer_factory.single_axis()

  def _detector(self):
    '''Return a model for a simple detector, presuming no one has
    one of these on a two-theta stage. Assert that the beam centre is
    provided in the Mosflm coordinate frame.'''

    distance = float(self._header_dictionary['DISTANCE'])
    beam_x = float(self._header_dictionary['BEAM_CENTER_X'])
    beam_y = float(self._header_dictionary['BEAM_CENTER_Y'])
    pixel_size = float(self._header_dictionary['PIXEL_SIZE'])
    image_size = (float(self._header_dictionary['SIZE1']),
                  float(self._header_dictionary['SIZE2']))
    overload = 65535
    underload = 1

    return self._detector_factory.simple(
        'CCD', distance, (beam_y, beam_x), '+x', '-y',
        (pixel_size, pixel_size), image_size, (underload, overload), [])

  def _beam(self):
    '''Return a simple model for the beam.'''

    wavelength = float(self._header_dictionary['WAVELENGTH'])

    return self._beam_factory.simple(wavelength)

  def _scan(self):
    '''Return the scan information for this image.'''
    import calendar

    format = self._scan_factory.format('SMV')
    exposure_time = float(self._header_dictionary['TIME'])
    epoch = None

    # PST, PDT timezones not recognised by default...

    epoch = 0
    try:
      date_str = self._header_dictionary['DATE']
      date_str = date_str.replace('PST', '').replace('PDT', '')
    except KeyError, e:
      date_str = ''
    for format_string in ['%a %b %d %H:%M:%S %Y', '%a %b %d %H:%M:%S %Z %Y']:
      try:
        epoch = calendar.timegm(time.strptime(date_str, format_string))
        break
      except ValueError, e:
        pass
class FormatSMVADSC(FormatSMV):
  '''A class for reading SMV format ADSC images, and correctly constructing
  a model for the experiment from this.'''

  @staticmethod
  def understand(image_file):
    '''Check to see if this looks like an ADSC SMV format image, i.e. we
    can make sense of it. Essentially that will be if it contains all of
    the keys we are looking for and not some we are not (i.e. that belong
    to a Rigaku Saturn.)'''

    size, header = FormatSMV.get_smv_header(image_file)

    # do not understand JHSim images
    if header.get('BEAMLINE') == 'fake': return False

    # do not understand Timepix_SU images
    if header.get('BEAMLINE') == 'TimePix_SU': return False

    # this used to include TIME
    wanted_header_items = ['BEAM_CENTER_X', 'BEAM_CENTER_Y',
                           'DISTANCE', 'WAVELENGTH', 'PIXEL_SIZE',
                           'OSC_START', 'OSC_RANGE', 'SIZE1', 'SIZE2',
                           'BYTE_ORDER']

    for header_item in wanted_header_items:
      if not header_item in header:
        return False

    unwanted_header_items = ['DTREK_DATE_TIME']

    for header_item in unwanted_header_items:
      if header_item in header:
        return False

    return True

  def __init__(self, image_file, **kwargs):
    '''Initialise the image structure from the given file, including a
    proper model of the experiment.'''

    from dxtbx import IncorrectFormatError
    if not self.understand(image_file):
      raise IncorrectFormatError(self, image_file)

    FormatSMV.__init__(self, image_file, **kwargs)

    return

  def _start(self):

    FormatSMV._start(self)

  def detectorbase_start(self):
    if not hasattr(self, "detectorbase") or self.detectorbase is None:
      from iotbx.detectors import SMVImage
      self.detectorbase = SMVImage(self._image_file)
      self.detectorbase.open_file = self.open_file
      self.detectorbase.readHeader()

  def _goniometer(self):
    '''Return a model for a simple single-axis goniometer. This should
    probably be checked against the image header.'''

    return self._goniometer_factory.single_axis()

  def _detector(self):
    '''Return a model for a simple detector, presuming no one has
    one of these on a two-theta stage. Assert that the beam centre is
    provided in the Mosflm coordinate frame.'''

    distance = float(self._header_dictionary['DISTANCE'])
    beam_x = float(self._header_dictionary['BEAM_CENTER_X'])
    beam_y = float(self._header_dictionary['BEAM_CENTER_Y'])
    pixel_size = float(self._header_dictionary['PIXEL_SIZE'])
    image_size = (float(self._header_dictionary['SIZE1']),
                  float(self._header_dictionary['SIZE2']))
    overload = 65535
    underload = 1

    return self._detector_factory.simple(
        'CCD', distance, (beam_y, beam_x), '+x', '-y',
        (pixel_size, pixel_size), image_size, (underload, overload), [])

  def _beam(self):
    '''Return a simple model for the beam.'''

    wavelength = float(self._header_dictionary['WAVELENGTH'])

    return self._beam_factory.simple(wavelength)

  def _scan(self):
    '''Return the scan information for this image.'''
    import calendar

    format = self._scan_factory.format('SMV')
    exposure_time = float(self._header_dictionary['TIME'])
    epoch = None

    # PST, PDT timezones not recognised by default...

    epoch = 0
    try:
      date_str = self._header_dictionary['DATE']
      date_str = date_str.replace('PST', '').replace('PDT', '')
    except KeyError, e:
      date_str = ''
    for format_string in ['%a %b %d %H:%M:%S %Y', '%a %b %d %H:%M:%S %Z %Y']:
      try:
        epoch = calendar.timegm(time.strptime(date_str, format_string))
        break
      except ValueError, e:
        pass
Beispiel #18
0
class FormatSMVADSC(FormatSMV):
    '''A class for reading SMV format ADSC images, and correctly constructing
  a model for the experiment from this.'''
    @staticmethod
    def understand(image_file):
        '''Check to see if this looks like an ADSC SMV format image, i.e. we
    can make sense of it. Essentially that will be if it contains all of
    the keys we are looking for and not some we are not (i.e. that belong
    to a Rigaku Saturn.)'''

        size, header = FormatSMV.get_smv_header(image_file)

        # do not understand JHSim images
        if header.get('BEAMLINE') == 'fake': return False

        # do not understand Timepix_SU images
        if header.get('BEAMLINE', '').upper() == 'TIMEPIX_SU': return False

        # this used to include TIME
        wanted_header_items = [
            'BEAM_CENTER_X', 'BEAM_CENTER_Y', 'DISTANCE', 'WAVELENGTH',
            'PIXEL_SIZE', 'OSC_START', 'OSC_RANGE', 'SIZE1', 'SIZE2',
            'BYTE_ORDER'
        ]

        for header_item in wanted_header_items:
            if not header_item in header:
                return False

        unwanted_header_items = ['DTREK_DATE_TIME']

        for header_item in unwanted_header_items:
            if header_item in header:
                return False

        return True

    def __init__(self, image_file, **kwargs):
        '''Initialise the image structure from the given file, including a
    proper model of the experiment.'''

        from dxtbx import IncorrectFormatError
        if not self.understand(image_file):
            raise IncorrectFormatError(self, image_file)

        FormatSMV.__init__(self, image_file, **kwargs)

    def _start(self):

        FormatSMV._start(self)

    def detectorbase_start(self):
        if not hasattr(self, "detectorbase") or self.detectorbase is None:
            from iotbx.detectors import SMVImage
            self.detectorbase = SMVImage(self._image_file)
            self.detectorbase.open_file = self.open_file
            self.detectorbase.readHeader()

    def _goniometer(self):
        '''Return a model for a simple single-axis goniometer. Invert the axis if
    the rotation range is negative.'''

        if float(self._header_dictionary['OSC_RANGE']) > 0:
            return self._goniometer_factory.single_axis()
        else:
            return self._goniometer_factory.single_axis_reverse()

    def _adsc_module_gain(self, model=None):
        '''Return an appropriate gain value in ADU per captured X-ray for an
    ADSC CCD module. If the model is None (unknown) then make a guess based
    on header details'''

        # Values are based on a combination of manufacturer datasheets,
        # James Holton's list at http://bl831.als.lbl.gov/xtalsize.html and
        # empirical guesswork based on spot finding results. The accuracy should
        # not be expected to be better than 20% and indeed may be worse than that.
        # Values may refer to gain in ADU per incident photon rather than the
        # more appropriate ADU per captured photon.

        # Get the binning level
        bin_lev = str(self._header_dictionary.get('BIN'))

        if model is None:
            # guesswork based on the header details
            npx1 = int(self._header_dictionary['SIZE1'])
            npx2 = int(self._header_dictionary['SIZE2'])
            px_sz = float(self._header_dictionary['PIXEL_SIZE'])
            if npx1 == npx2 == 2304 and px_sz == 0.0816:
                model = 'Q4'  # or Q4R
            elif npx1 == npx2 == 4096 and px_sz == 0.0512:
                model = 'Q210'  # or Q210R bin none or 1x1
            elif npx1 == npx2 == 2048 and px_sz == 0.1024:
                model = 'Q210'  # or Q210R bin 2x2
                bin_lev = '2x2'
            elif npx1 == npx2 == 6144 and px_sz == 0.0512:
                model = 'Q315'  # or Q315R bin none or 1x1
            elif npx1 == npx2 == 3072 and px_sz == 0.1024:
                model = 'Q315'  # or Q315R bin 2x2
                bin_lev = '2x2'
            elif npx1 == npx2 == 4168 and px_sz == 0.0648:
                model = 'Q270'  # or Q270R bin none or 1x1
            elif npx1 == npx2 == 2084 and px_sz == 0.0324:
                model = 'Q270'  # or Q270R bin 2x2
                bin_lev = '2x2'
            else:
                # Unidentified model
                return 1.0

        model = model.upper()
        if model.startswith('Q270'):
            # Don't know the difference between binning modes in this case
            return 2.8

        if model.startswith('Q4'):
            # https://web.archive.org/web/20051224172252/http://www.adsc-xray.com:80/prod_compare.html
            # Don't know the difference between binning modes in this case
            return 1.25

        # Get binning mode HW/SW
        bin_type = self._header_dictionary.get('BIN_TYPE')

        if bin_lev.upper() == 'NONE':
            bin_type = None
        elif bin_type != 'HW':
            bin_type = 'SW'

        if bin_type == 'SW':
            #return 0.6 This does not look believable. Default to 2.4 instead
            return 2.4

        if bin_type != 'HW':  # assume unbinned
            return 2.4
        elif model.endswith('R'):  # e.g. Q315r, HW binning
            return 1.8
        else:  # e.g. Q315, HW binning
            return 2.4

    def _detector(self):
        '''Return a model for a simple detector, presuming no one has
    one of these on a two-theta stage. Assert that the beam centre is
    provided in the Mosflm coordinate frame.'''

        distance = float(self._header_dictionary['DISTANCE'])
        beam_x = float(self._header_dictionary['BEAM_CENTER_X'])
        beam_y = float(self._header_dictionary['BEAM_CENTER_Y'])
        pixel_size = float(self._header_dictionary['PIXEL_SIZE'])
        image_size = (float(self._header_dictionary['SIZE1']),
                      float(self._header_dictionary['SIZE2']))
        if 'IMAGE_PEDESTAL' in self._header_dictionary:
            pedestal = int(self._header_dictionary['IMAGE_PEDESTAL'])
        else:
            pedestal = 0

        overload = 65535 - pedestal
        underload = pedestal - 1

        return self._detector_factory.simple('CCD',
                                             distance, (beam_y, beam_x),
                                             '+x',
                                             '-y', (pixel_size, pixel_size),
                                             image_size, (underload, overload),
                                             [],
                                             gain=self._adsc_module_gain())

    def _beam(self):
        '''Return a simple model for the beam.'''

        wavelength = float(self._header_dictionary['WAVELENGTH'])

        return self._beam_factory.simple(wavelength)

    def _scan(self):
        '''Return the scan information for this image.'''
        import calendar

        format = self._scan_factory.format('SMV')
        exposure_time = float(self._header_dictionary['TIME'])
        epoch = None

        # PST, PDT timezones not recognised by default...

        epoch = 0
        try:
            date_str = self._header_dictionary['DATE']
            date_str = date_str.replace('PST', '').replace('PDT', '')
        except KeyError:
            date_str = ''
        for format_string in [
                '%a %b %d %H:%M:%S %Y', '%a %b %d %H:%M:%S %Z %Y'
        ]:
            try:
                epoch = calendar.timegm(time.strptime(date_str, format_string))
                break
            except ValueError:
                pass

        # assert(epoch)
        osc_start = float(self._header_dictionary['OSC_START'])
        osc_range = abs(float(self._header_dictionary['OSC_RANGE']))

        return self._scan_factory.single(self._image_file, format,
                                         exposure_time, osc_start, osc_range,
                                         epoch)

    def get_raw_data(self):
        '''Get the pixel intensities (i.e. read the image and return as a
    flex array of integers.)'''

        from boost.python import streambuf
        from dxtbx import read_uint16, read_uint16_bs, is_big_endian
        from scitbx.array_family import flex
        assert (len(self.get_detector()) == 1)
        panel = self.get_detector()[0]
        size = panel.get_image_size()
        f = FormatSMVADSC.open_file(self._image_file, 'rb')
        f.read(self._header_size)

        if self._header_dictionary['BYTE_ORDER'] == 'big_endian':
            big_endian = True
        else:
            big_endian = False

        if big_endian == is_big_endian():
            raw_data = read_uint16(streambuf(f), int(size[0] * size[1]))
        else:
            raw_data = read_uint16_bs(streambuf(f), int(size[0] * size[1]))

        image_size = panel.get_image_size()
        raw_data.reshape(flex.grid(image_size[1], image_size[0]))

        # if we subtract PEDESTAL is this still raw?
        if 'IMAGE_PEDESTAL' in self._header_dictionary:
            raw_data -= int(self._header_dictionary['IMAGE_PEDESTAL'])

        return raw_data
class FormatSMVTimePix_SU(FormatSMV):
  '''A class for reading SMV format images from the RED software, where the
  main identifying feature is BEAMLINE=TimePix_SU. This is intended to
  construct a model for an electron diffraction experiment using a microscope
  at Stockholm University.'''

  @staticmethod
  def understand(image_file):
    '''Check to see if this looks like an ADSC SMV format image, i.e. we
    can make sense of it. Essentially that will be if it contains all of
    the keys we are looking for and not some we are not (i.e. that belong
    to a Rigaku Saturn.)'''

    size, header = FormatSMV.get_smv_header(image_file)

    # only recognise TimePix_SU
    if header.get('BEAMLINE') != 'TimePix_SU': return False

    # check the header contains the things we're going to use
    wanted_header_items = ['BEAM_CENTER_X', 'BEAM_CENTER_Y',
                           'DISTANCE', 'WAVELENGTH', 'PIXEL_SIZE',
                           'OSC_START', 'OSC_RANGE', 'PHI', 'SIZE1', 'SIZE2',
                           'BYTE_ORDER', 'DETECTOR_SN']
    for header_item in wanted_header_items:
      if not header_item in header:
        return False

    # check the pixel size is 55 microns
    if not float(header['PIXEL_SIZE']) == 0.055: return False

    # check there are 512*512 pixels
    if not (header['SIZE1']) == '512': return False
    if not (header['SIZE2']) == '512': return False

    return True

  def __init__(self, image_file, **kwargs):
    '''Initialise the image structure from the given file, including a
    proper model of the experiment.'''

    from dxtbx import IncorrectFormatError
    if not self.understand(image_file):
      raise IncorrectFormatError(self, image_file)

    FormatSMV.__init__(self, image_file, **kwargs)

  def _start(self):

    FormatSMV._start(self)

  def detectorbase_start(self):
    if not hasattr(self, "detectorbase") or self.detectorbase is None:
      from iotbx.detectors import SMVImage
      self.detectorbase = SMVImage(self._image_file)
      self.detectorbase.open_file = self.open_file
      self.detectorbase.readHeader()

  def _goniometer(self):
    '''Return a model for a simple single-axis goniometer. For this beamline
    this should be close to the provided values and neither aligned with the
    detector slow or fast axes.'''

    return self._goniometer_factory.known_axis((-0.755, -0.656, 0.0))

  def _detector(self):
    '''4 panel detector, 55 micron pixels except for pixels at the outer
    edge of each chip, which are 165 microns wide.'''
    from scitbx import matrix

    # expect 55 mu pixels here, but make it general anyway
    pixel_size = tuple([float(self._header_dictionary['PIXEL_SIZE'])] * 2)
    image_size = (int(self._header_dictionary['SIZE1']),
                  int(self._header_dictionary['SIZE2']))
    panel_size = tuple([int(e/2) for e in image_size])

    # outer pixels have three times the width
    panel_size_mm = (pixel_size[0] * 3 + (panel_size[0] - 2) * pixel_size[0],
                     pixel_size[1] * 3 + (panel_size[1] - 2) * pixel_size[1])
    image_size_mm = (panel_size_mm[0] * 2, panel_size_mm[1] * 2)
    trusted_range = (-1, 65535)
    material = 'Si'
    thickness = 0.3 # assume 300 mu thick

    # Initialise detector frame
    fast = matrix.col((1.0, 0.0, 0.0))
    slow = matrix.col((0.0, -1.0, 0.0))
    beam_centre = (float(self._header_dictionary['BEAM_CENTER_X']),
                   float(self._header_dictionary['BEAM_CENTER_Y']))

    bx_px, by_px = beam_centre
    # the beam centre is in pixels. We want to convert to mm, taking the
    # different size of outer pixels into account. Use this local function
    # to do that
    def px_to_mm(px, px_size_1d, panel_size_1d):
      mm = 0
      if px > 1: # add first outer pixel
        mm += px_size_1d * 3
      else: # or fraction of first outer pixel
        mm += px * px_size_1d * 3
        return mm

      if px > panel_size_1d - 1: # add full panel of inner pixels
        mm += (panel_size_1d - 2) * px_size_1d
      else: # or fraction of inner pixels
        mm += (px - 1) * px_size_1d
        return mm

      if px > panel_size_1d: # add second outer pixel
        mm += px_size_1d * 3
      else: # or fraction of second outer pixel
        mm += (px - (panel_size_1d - 1)) * px_size_1d * 3
        return mm

      if px > panel_size_1d + 1: # add first outer pixel of second panel
        mm += px_size_1d * 3
      else: # or fraction of first outer pixel of second panel
        mm += (px - panel_size_1d) * px_size_1d * 3
        return mm

      if px > (2 * panel_size_1d - 1): # add second full panel of inner pixels
        mm += (panel_size_1d - 2) * px_size_1d
        # plus remaining fraction of the second outer pixel
        mm += (px - (2 * panel_size_1d - 1)) * px_size_1d * 3
      else: # or fraction of inner pixels of the second panel
        mm += (px - panel_size_1d - 1) * px_size_1d
      return mm

    bx_mm = px_to_mm(bx_px, pixel_size[0], panel_size[0])
    by_mm = px_to_mm(by_px, pixel_size[1], panel_size[1])
    beam_centre_mm = (bx_mm, by_mm)

    # the beam centre is defined from the origin along fast, slow. To determine
    # the lab frame origin we place the beam centre down the -z axis
    cntr = matrix.col((0.0, 0.0, -1 * float(self._header_dictionary['DISTANCE'])))
    orig = cntr - bx_mm * fast - by_mm * slow

    d = Detector()

    root = d.hierarchy()
    root.set_local_frame(
      fast.elems,
      slow.elems,
      orig.elems)

    self.coords = {}
    panel_idx = 0

    # set panel extent in pixel numbers and x, y mm shifts. Note that the
    # outer pixels are 0.165 mm in size. These are excluded from the panel
    # extents.
    pnl_data = []
    pnl_data.append({'xmin':1, 'ymin':1,
                     'xmax':255, 'ymax':255,
                     'xmin_mm': 1 * 0.165,
                     'ymin_mm': 1 * 0.165})
    pnl_data.append({'xmin':257, 'ymin':1,
                     'xmax':511, 'ymax':255,
                     'xmin_mm': 3 * 0.165 + (511 - 257) * pixel_size[0],
                     'ymin_mm': 1 * 0.165})
    pnl_data.append({'xmin':1, 'ymin':257,
                     'xmax':255, 'ymax':511,
                     'xmin_mm': 1 * 0.165,
                     'ymin_mm': 3 * 0.165 + (511 - 257) * pixel_size[1]})
    pnl_data.append({'xmin':257, 'ymin':257,
                     'xmax':511, 'ymax':511,
                     'xmin_mm': 3 * 0.165 + (511 - 257) * pixel_size[0],
                     'ymin_mm': 3 * 0.165 + (511 - 257) * pixel_size[1]})

    # redefine fast, slow for the local frame
    fast = matrix.col((1.0, 0.0, 0.0))
    slow = matrix.col((0.0, 1.0, 0.0))

    for ipanel, pd in enumerate(pnl_data):
      xmin = pd['xmin']
      xmax = pd['xmax']
      ymin = pd['ymin']
      ymax = pd['ymax']
      xmin_mm = pd['xmin_mm']
      ymin_mm = pd['ymin_mm']

      origin_panel = fast * xmin_mm + slow * ymin_mm

      panel_name = "Panel%d" % panel_idx
      panel_idx += 1

      p = d.add_panel()
      p.set_type('SENSOR_PAD')
      p.set_name(panel_name)
      p.set_raw_image_offset((xmin, ymin))
      p.set_image_size((xmax-xmin, ymax-ymin))
      p.set_trusted_range(trusted_range)
      p.set_pixel_size((pixel_size[0], pixel_size[1]))
      p.set_thickness(thickness)
      p.set_material('Si')
      #p.set_mu(mu)
      #p.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0))
      p.set_local_frame(
        fast.elems,
        slow.elems,
        origin_panel.elems)
      p.set_raw_image_offset((xmin, ymin))
      self.coords[panel_name] = (xmin, ymin, xmax, ymax)

    return d

  def _beam(self):
    '''Return an unpolarized beam model.'''

    wavelength = float(self._header_dictionary['WAVELENGTH'])

    return self._beam_factory.make_polarized_beam(
        sample_to_source=(0.0, 0.0, 1.0),
        wavelength=wavelength,
        polarization=(0, 1, 0),
        polarization_fraction=0.5)

  def _scan(self):
    '''Return the scan information for this image.'''
    import calendar

    format = self._scan_factory.format('SMV')
    exposure_time = float(self._header_dictionary['TIME'])
    epoch = None

    # PST, PDT timezones not recognised by default...

    epoch = 0
    try:
      date_str = self._header_dictionary['DATE']
      date_str = date_str.replace('PST', '').replace('PDT', '')
    except KeyError:
      date_str = ''
    for format_string in ['%a %b %d %H:%M:%S %Y', '%a %b %d %H:%M:%S %Z %Y']:
      try:
        epoch = calendar.timegm(time.strptime(date_str, format_string))
        break
      except ValueError:
        pass

    # assert(epoch)
    osc_start = float(self._header_dictionary['OSC_START'])
    osc_range = float(self._header_dictionary['OSC_RANGE'])

    return self._scan_factory.single(
        self._image_file, format, exposure_time,
        osc_start, osc_range, epoch)

  def get_raw_data(self):
    '''Get the pixel intensities (i.e. read the image and return as a
    flex array of integers.)'''

    from boost.python import streambuf
    from dxtbx import read_uint16, read_uint16_bs, is_big_endian
    from scitbx.array_family import flex
    f = self.open_file(self._image_file, 'rb')
    f.read(self._header_size)

    if self._header_dictionary['BYTE_ORDER'] == 'big_endian':
      big_endian = True
    else:
      big_endian = False

    if big_endian == is_big_endian():
      raw_data = read_uint16(streambuf(f), int(512 * 512))
    else:
      raw_data = read_uint16_bs(streambuf(f), int(512 * 512))

    image_size = (512,512)
    raw_data.reshape(flex.grid(image_size[1], image_size[0]))

    # split into separate panels
    self._raw_data = []
    d = self.get_detector()
    for panel in d:
      xmin, ymin, xmax, ymax = self.coords[panel.get_name()]
      self._raw_data.append(raw_data[ymin:ymax,xmin:xmax])

    return tuple(self._raw_data)