text: # coding: utf-8
  
  import sys
  import os
  import pickle as _pickle
  from importlib.resources import read_binary
  os.environ["OMP_NUM_THREADS"] = "1"
  import numpy as _np
  
  import skimage as _skimage
  from skimage.color.adapt_rgb import adapt_rgb as _adapt_rgb
  import skimage.io as _skimageio
  from skimage.measure import label as _label
  from skimage import filters as _filters
  
  # load a dictionary with the binary pattern for each numeric digit
  # _digits_fname = _resource_filename(__name__, 'data/DIGITDICT.p')
  # DIGITDICT = _pickle.load(open(_digits_fname, "rb"), encoding='bytes')
  contents = read_binary('stardot_overlay.data', 'DIGITDICT.p')
  DIGITDICT = _pickle.loads(contents)
  
  # load a dictionary with the binary pattern for each character
  # _digits_full_fname = _resource_filename(__name__, 'data/DIGITDICT_FULL.p')
  # DIGITDICT_FULL = _pickle.load(open(_digits_full_fname, "rb"),
  #                              encoding='latin1')
  contents = read_binary('stardot_overlay.data', 'DIGITDICT_FULL.p')
  DIGITDICT_FULL = _pickle.loads(contents)
  
  def _get_lines(fname):
      """
      Returns the portions of the image cooresponding to the
      first four lines of text in the header.
      These are returned as 3D numpy arrays.
      """
      @_adapt_rgb(_skimage.color.adapt_rgb.hsv_value)
      def sobel_hsv(image):
          return _filters.sobel(image)
  
      # read in image file
      data = _skimageio.imread(fname)
  
      # select default ranges for the first 4 lines (this may vary from site to site)
      # possibly make these input parameters
      l1_range = data[0:27, 0:850, :]
      l2_range = data[28:55, 0:500, :]
      l3_range = data[56:83, 0:350, :]
      l4_range = data[84:111, 0:350, :]
  
      # Look for a right edge in the image in the default ranges which
      # would indicate an end of the overlay in the x direction
      intensity_limit = 2
      l1_edges = _np.sum(
          _skimage.exposure.rescale_intensity(1 - sobel_hsv(l1_range)),
          axis=2) < intensity_limit
      l2_edges = _np.sum(
          _skimage.exposure.rescale_intensity(1 - sobel_hsv(l2_range)),
          axis=2) < intensity_limit
      l3_edges = _np.sum(
          _skimage.exposure.rescale_intensity(1 - sobel_hsv(l3_range)),
          axis=2) < intensity_limit
      l4_edges = _np.sum(
          _skimage.exposure.rescale_intensity(1 - sobel_hsv(l4_range)),
          axis=2) < intensity_limit
  
      # try to adjust the default ranges if an edge was found
      sumlim = 25
      try:
          l1_right_edge = 0 + \
              _np.where(_np.sum(l1_edges, axis=0) >= sumlim)[0].max()
      except ValueError:
          l1_right_edge = 850
      if l1_right_edge < 2:
          l1_right_edge = 850
  
      try:
          l2_right_edge = 0 + \
              _np.where(_np.sum(l2_edges, axis=0) >= sumlim)[0].max()
      except ValueError:
          l2_right_edge = 500
      if l2_right_edge < 2:
          l2_right_edge = 500
  
      try:
          l3_right_edge = 0 + \
              _np.where(_np.sum(l3_edges, axis=0) >= sumlim)[0].max()
      except ValueError:
          l3_right_edge = 350
      if l3_right_edge < 2:
          l3_right_edge = 350
  
      try:
          l4_right_edge = 0 + \
              _np.where(_np.sum(l4_edges, axis=0) >= sumlim)[0].max()
      except ValueError:
          l4_right_edge = 350
      if l4_right_edge < 2:
          l4_right_edge = 350
  
      # extract an array for each of the first four lines
      line1 = data[0:27, :l1_right_edge, :]
      line2 = data[28:55, :l2_right_edge, :]
      line3 = data[56:83, :l3_right_edge, :]
      line4 = data[84:111, :l4_right_edge, :]
      
      return line1, line2, line3, line4
  
  
  def _get_binary(raw_data):
      """
      Given an image array returns the image converted to a
      "cleaned up" binary (i.e. only values of 1 or 0) array.
      The returned array is actually a boolean array!
      """
      #try:
      #    gray_data = _skimage.color.rgb2gray(raw_data)
      #except:
      #    gray_data = raw_data
  
      try:
          thresh = _skimage.filters.threshold_otsu(raw_data[:, :, 0])
          binary = raw_data[:, :, 0] > thresh
          #binary = binary[:, :, 0]
      except ValueError:
          print('valueerror')
          binary = _np.ones(raw_data.shape).astype('bool')
  
      return binary
  
  
  def _segment(binary):
      """
      Parses a chunk of text provided in a boolean numpy array.
      Returns:
          a tuple with a list of unique tags for each each individual 'letter'
          a numpy array with these tags inserted where the 'letters' are.
          
      The skimage.measure.label() function segments an array into "connected" parts,
      the idea being the each character in the images is a group of connected pixels.
      I'm not sure how characters which are not all connected (e.g. "i", ":") are 
      handled.  The code below connects these "unconnected" chars by looking at the
      transpose array!
      """
      label_image = _label(binary)
  
      digit_labels = []
      for column in label_image.T:
          col_vals = _np.unique(column)
          if len(col_vals) == 1:
              pass
          elif len(col_vals) == 2 and col_vals[1] not in digit_labels:
              digit_labels.append(col_vals[1])
          elif len(col_vals) == 3:
              label_image[label_image == col_vals[2]] = col_vals[1]
              if col_vals[1] not in digit_labels:
                  digit_labels.append(col_vals[1])
  
      return digit_labels, label_image
  
  def _get_digit(binary, label_image, digit):
      """
      Extracts a 'character' from a labeled image corresponding to the
      specified tag.  returns a binary numpy array with just that
      character's pixels.
      """
      ymin, ymax = _np.where(label_image == digit)[0].min(), \
          _np.where(label_image == digit)[0].max() + 1
      xmin, xmax = _np.where(label_image == digit)[1].min(), \
          _np.where(label_image == digit)[1].max() + 1
  
      return binary[ymin:ymax, xmin:xmax]
  
  
  def _extract_digits(binary):
      """
      Given a binary chunk (A single line) of text
      Returns the numeric digits it contains.
  
      Ignores letters and noise.
      """
      digit_labels, labeled_image = _segment(binary)
  
      ans = ""
      for digit_label in digit_labels:
          try:
              num = _return_char(_get_digit(binary, labeled_image,
                                            digit_label))
          except ValueError:
              num = None
              
          if num is not None:
              ans += num
              
      return ans
  
  
  def _return_char(num_image):
      """
      Given a binary array of a single digit returns the digit (string
      character) of the matching image in our DIGITDICT
      """
  
      # check image (segment) against the full dictionary of
      # characters and return first match
      for digit, digit_image in DIGITDICT_FULL.items():
          if _np.array_equal(digit_image, num_image):
              return digit
  
      # if no match found then return None
      return None
  
  
  def get_header_contents(fname):
      """
      """
      line1, line2, line3, line4 = _get_lines(fname)
  
      output = {}
      for line in [('line1', line1),
                   ('line2', line2),
                   ('line3', line3),
                   ('line4', line4), ]:
          
          if len(_np.unique(line[1])) > 1:
              line_binary = _get_binary(line[1])
              output[line[0]] = _extract_digits(line_binary)
          else:
              output[line[0]] = ''
  
      return output
  
  def get_exposure(fname):
  
      header_contents = get_header_contents(fname)
  
      # 4 line header, readingma style
      if "Exposure:" in header_contents['line4']:
          exposure_line = header_contents['line4']
          return exposure_line[exposure_line.index(':') + 1:]
      elif "xposure:" in header_contents['line4']:   # turkeypointdbf
          exposure_line = header_contents['line4']
          return exposure_line[exposure_line.index(':') + 1:]
      elif "Exposure:" in header_contents['line3']:  # quickbird
          exposure_line = header_contents['line3']
          return exposure_line[exposure_line.index(':') + 1:]
      elif "Expsure:" in header_contents['line3']:  # quickbird
          exposure_line = header_contents['line3']
          return exposure_line[exposure_line.index(':') + 1:]
      elif "Exposure:" in header_contents['line2']:  # might as well
          exposure_line = header_contents['line2']
          return exposure_line[exposure_line.index(':') + 1:]
      elif "Exposure:" in header_contents['line1']:  # harvard style
          exposure_line = header_contents['line1']
          return exposure_line[exposure_line.index('Exposure:') + 9:]
      else:
          sys.stderr.write("Error extracting exposure value.\n")
          return None
  
  