# Path: ifcb/data/adc.py
# class SCHEMA_VERSION_1(object):
#     """
#     IFCB revision 1 schema.
#     """
#     _name = 'v1'
#     _cols = range(15)
#     TRIGGER = 0
#     PROCESSING_END_TIME = 1
#     FLUORESCENCE_LOW = 2
#     FLUORESCENCE_HIGH = 3
#     SCATTERING_LOW = 4
#     SCATTERING_HIGH = 5
#     COMPARATOR_PULSE = 6
#     TRIGGER_OPEN_TIME = 7
#     FRAME_GRAB_TIME = 8
#     ROI_X = 9
#     ROI_Y = 10
#     ROI_WIDTH = 11
#     ROI_HEIGHT = 12
#     START_BYTE = 13
#     VALVE_STATUS = 14
# 
# Path: ifcb/data/stitching.py
# class InfilledImages(BaseDictlike):
#     """
#     Wraps a bin's "images" property and provides access to infilled
#     images. Images that are not infilled are passed through.
# 
#     Dict-like interface excludes from its keys target numbers that
#     refer to the second ROI in a stitched pair.
#     """
#     def __init__(self, the_bin):
#         self.bin = the_bin
#         self.stitcher = Stitcher(the_bin)
#         self.infiller = Infiller(the_bin)
#     def keys(self):
#         """
#         Yield the target number of each ROI that is not the second
#         ROI in a stitched pair.
#         """
#         for k in self.bin.images:
#             if k not in self.stitcher.excluded_targets():
#                 yield k
#     def has_key(self, target_number):
#         """
#         Exclude each target number from the bin's images that is
#         second ROI from a stitched pair.
#         """
#         in_bin = target_number in self.bin
#         excluded = target_number in self.stitcher.excluded_targets()
#         return in_bin and not excluded
#     def __getitem__(self, target_number):
#         if target_number in self.stitcher:
#             # stitch the images
#             raw_stitch = self.stitcher[target_number]
#             # construct infill
#             infill = self.infiller[target_number]
#             # sum the stitched and infill images
#             infilled = raw_stitch.filled(0) + infill.filled(0)
#             return infilled
#         else:
#             # this is not a stitched image
#             return self.bin.images[target_number]
#     @lru_cache()
#     def _shapes(self):
#         schema = self.bin.schema
#         h_attr = '_{}'.format(schema.ROI_HEIGHT + 1)
#         w_attr = '_{}'.format(schema.ROI_WIDTH + 1)
#         shapes = {}
#         for row in self.bin.images_adc.itertuples():
#             target_number = row.Index
#             h = getattr(row, h_attr)
#             w = getattr(row, w_attr)
#             shapes[target_number] = (h, w)
#         for target_number in self.stitcher:
#             shapes[target_number] = self.stitcher.shape(target_number)
#         return shapes
#     def shape(self, target_number):
#         return self._shapes()[target_number]
#     # convenience methods
#     def raw_stitch(self, target_number):
#         return self.stitcher[target_number]


from functools import lru_cache
from skimage.transform import resize
from rectpack import newPacker, SORT_AREA
from rectpack.guillotine import GuillotineBafSlas
from ifcb.data.adc import SCHEMA_VERSION_1
from ifcb.data.stitching import InfilledImages
import math
import numpy as np
import pandas as pd
        hs, ws, ix = [], [], []
        with self.bin:
            ii = InfilledImages(self.bin)
            for target_number in ii:
                h, w = ii.shape(target_number)
                hs.append(math.floor(h * self.scale))
                ws.append(math.floor(w * self.scale))
                ix.append(target_number)
        return zip(hs, ws, ix)
    def pack(self, max_pages=20):
        if self.coordinates is not None:
            return self.coordinates
        page_h, page_w = self.shape
        pages = [(page_h - 1, page_w - 1) for _ in range(max_pages)]
        packer = newPacker(sort_algo=SORT_AREA, rotation=False, pack_algo=GuillotineBafSlas)
        for r in self._shapes():
            packer.add_rect(*r)
        for p in pages:
            packer.add_bin(*p)
        packer.pack()
        COLS = ['page', 'y', 'x', 'h', 'w', 'roi_number']
        self.coordinates = pd.DataFrame(packer.rect_list(), columns=COLS)
        return self.coordinates
    def page(self, page=0):
        df = self.pack()
        page_h, page_w = self.shape
        page_image = np.zeros((page_h, page_w), dtype=np.uint8) + self.bg_color
        sdf = df[df.page == page]
        with self.bin:
            if self.bin.schema == SCHEMA_VERSION_1:

# Path: ifcb/data/adc.py
# class SCHEMA_VERSION_1(object):
#     """
#     IFCB revision 1 schema.
#     """
#     _name = 'v1'
#     _cols = range(15)
#     TRIGGER = 0
#     PROCESSING_END_TIME = 1
#     FLUORESCENCE_LOW = 2
#     FLUORESCENCE_HIGH = 3
#     SCATTERING_LOW = 4
#     SCATTERING_HIGH = 5
#     COMPARATOR_PULSE = 6
#     TRIGGER_OPEN_TIME = 7
#     FRAME_GRAB_TIME = 8
#     ROI_X = 9
#     ROI_Y = 10
#     ROI_WIDTH = 11
#     ROI_HEIGHT = 12
#     START_BYTE = 13
#     VALVE_STATUS = 14
# 
# Path: ifcb/data/stitching.py
# class InfilledImages(BaseDictlike):
#     """
#     Wraps a bin's "images" property and provides access to infilled
#     images. Images that are not infilled are passed through.
# 
#     Dict-like interface excludes from its keys target numbers that
#     refer to the second ROI in a stitched pair.
#     """
#     def __init__(self, the_bin):
#         self.bin = the_bin
#         self.stitcher = Stitcher(the_bin)
#         self.infiller = Infiller(the_bin)
#     def keys(self):
#         """
#         Yield the target number of each ROI that is not the second
#         ROI in a stitched pair.
#         """
#         for k in self.bin.images:
#             if k not in self.stitcher.excluded_targets():
#                 yield k
#     def has_key(self, target_number):
#         """
#         Exclude each target number from the bin's images that is
#         second ROI from a stitched pair.
#         """
#         in_bin = target_number in self.bin
#         excluded = target_number in self.stitcher.excluded_targets()
#         return in_bin and not excluded
#     def __getitem__(self, target_number):
#         if target_number in self.stitcher:
#             # stitch the images
#             raw_stitch = self.stitcher[target_number]
#             # construct infill
#             infill = self.infiller[target_number]
#             # sum the stitched and infill images
#             infilled = raw_stitch.filled(0) + infill.filled(0)
#             return infilled
#         else:
#             # this is not a stitched image
#             return self.bin.images[target_number]
#     @lru_cache()
#     def _shapes(self):
#         schema = self.bin.schema
#         h_attr = '_{}'.format(schema.ROI_HEIGHT + 1)
#         w_attr = '_{}'.format(schema.ROI_WIDTH + 1)
#         shapes = {}
#         for row in self.bin.images_adc.itertuples():
#             target_number = row.Index
#             h = getattr(row, h_attr)
#             w = getattr(row, w_attr)
#             shapes[target_number] = (h, w)
#         for target_number in self.stitcher:
#             shapes[target_number] = self.stitcher.shape(target_number)
#         return shapes
#     def shape(self, target_number):
#         return self._shapes()[target_number]
#     # convenience methods
#     def raw_stitch(self, target_number):
#         return self.stitcher[target_number]


# Path: ifcb/viz/mosaic.py
from functools import lru_cache
from skimage.transform import resize
from rectpack import newPacker, SORT_AREA
from rectpack.guillotine import GuillotineBafSlas
from ifcb.data.adc import SCHEMA_VERSION_1
from ifcb.data.stitching import InfilledImages
import math
import numpy as np
import pandas as pd

        hs, ws, ix = [], [], []
        with self.bin:
            ii = InfilledImages(self.bin)
            for target_number in ii:
                h, w = ii.shape(target_number)
                hs.append(math.floor(h * self.scale))
                ws.append(math.floor(w * self.scale))
                ix.append(target_number)
        return zip(hs, ws, ix)
    def pack(self, max_pages=20):
        if self.coordinates is not None:
            return self.coordinates
        page_h, page_w = self.shape
        pages = [(page_h - 1, page_w - 1) for _ in range(max_pages)]
        packer = newPacker(sort_algo=SORT_AREA, rotation=False, pack_algo=GuillotineBafSlas)
        for r in self._shapes():
            packer.add_rect(*r)
        for p in pages:
            packer.add_bin(*p)
        packer.pack()
        COLS = ['page', 'y', 'x', 'h', 'w', 'roi_number']
        self.coordinates = pd.DataFrame(packer.rect_list(), columns=COLS)
        return self.coordinates
    def page(self, page=0):
        df = self.pack()
        page_h, page_w = self.shape
        page_image = np.zeros((page_h, page_w), dtype=np.uint8) + self.bg_color
        sdf = df[df.page == page]
        with self.bin:
            if self.bin.schema == SCHEMA_VERSION_1:

                ii = InfilledImages(self.bin)