Ejemplo n.º 1
0
def get_measurements_for_good_pipeline(nimages = 1, 
                                       group_numbers = None):
    '''Get an appropriately initialized measurements structure for the good pipeline'''
    path = os.path.join(example_images_directory(), "ExampleSBSImages")
    m = cpmeas.Measurements()
    if group_numbers is None:
        group_numbers = [1] * nimages
    group_indexes = [1]
    last_group_number = group_numbers[0]
    group_index = 1
    for group_number in group_numbers:
        if group_number == last_group_number:
            group_index += 1
        else:
            group_index = 1
        group_indexes.append(group_index)
    for i in range(1, nimages+1):
        filename = ("Channel2-%02d-%s-%02d.tif" % 
                    (i, "ABCDEFGH"[int((i-1) / 12)], ((i-1) % 12) + 1))
        url = pathname2url(os.path.join(path, filename))
        m[cpmeas.IMAGE, cpmeas.C_FILE_NAME + "_DNA", i] = filename
        m[cpmeas.IMAGE, cpmeas.C_PATH_NAME + "_DNA", i] = path
        m[cpmeas.IMAGE, cpmeas.C_URL+"_DNA", i] = url
        m[cpmeas.IMAGE, cpmeas.GROUP_NUMBER, i] = group_numbers[i-1]
        m[cpmeas.IMAGE, cpmeas.GROUP_INDEX, i] = group_indexes[i-1]
    pipeline = cpp.Pipeline()
    pipeline.loadtxt(StringIO(GOOD_PIPELINE))
    pipeline.write_pipeline_measurement(m)
    return m
        
Ejemplo n.º 2
0
 def run_workspace(self, path, load_as_type, 
                   series = None, index = None, channel = None,
                   single=False, rescaled=True):
     '''Run a workspace to load a file
     
     path - path to the file
     load_as_type - one of the LOAD_AS... constants
     series, index, channel - pick a plane from within a file
     
     returns the workspace after running
     '''
     n = N.NamesAndTypes()
     n.assignment_method.value = N.ASSIGN_ALL if single else N.ASSIGN_RULES
     n.single_image_provider.value = IMAGE_NAME
     n.single_load_as_choice.value = load_as_type
     n.single_rescale.value = rescaled
     n.assignments[0].image_name.value = IMAGE_NAME
     n.assignments[0].object_name.value = OBJECTS_NAME
     n.assignments[0].load_as_choice.value = load_as_type
     n.assignments[0].rescale.value = rescaled
     n.module_num = 1
     pipeline = cpp.Pipeline()
     pipeline.add_module(n)
     url = pathname2url(path)
     pathname, filename = os.path.split(path)
     m = cpmeas.Measurements(mode="memory")
     if load_as_type == N.LOAD_AS_OBJECTS:
         url_feature = cpmeas.C_OBJECTS_URL + "_" + OBJECTS_NAME
         path_feature = cpmeas.C_OBJECTS_PATH_NAME + "_" + OBJECTS_NAME
         file_feature = cpmeas.C_OBJECTS_FILE_NAME + "_" + OBJECTS_NAME
         series_feature = cpmeas.C_OBJECTS_SERIES + "_" + OBJECTS_NAME
         frame_feature = cpmeas.C_OBJECTS_FRAME + "_" + OBJECTS_NAME
         channel_feature = cpmeas.C_OBJECTS_CHANNEL + "_" + OBJECTS_NAME
     else:
         url_feature = cpmeas.C_URL + "_" + IMAGE_NAME
         path_feature = cpmeas.C_PATH_NAME + "_" + IMAGE_NAME
         file_feature = cpmeas.C_FILE_NAME + "_" + IMAGE_NAME
         series_feature = cpmeas.C_SERIES + "_" + IMAGE_NAME
         frame_feature = cpmeas.C_FRAME + "_" + IMAGE_NAME
         channel_feature = cpmeas.C_CHANNEL + "_" + IMAGE_NAME
         
     m.image_set_number = 1
     m.add_measurement(cpmeas.IMAGE, url_feature, url)
     m.add_measurement(cpmeas.IMAGE, path_feature, pathname)
     m.add_measurement(cpmeas.IMAGE, file_feature, filename)
     if series is not None:
         m.add_measurement(cpmeas.IMAGE, series_feature, series)
     if index is not None:
         m.add_measurement(cpmeas.IMAGE, frame_feature, index)
     if channel is not None:
         m.add_measurement(cpmeas.IMAGE, channel_feature, channel)
     m.add_measurement(cpmeas.IMAGE, cpmeas.GROUP_NUMBER, 1)
     m.add_measurement(cpmeas.IMAGE, cpmeas.GROUP_INDEX, 1)
     
     workspace = cpw.Workspace(pipeline, n, m,
                               N.cpo.ObjectSet(),
                               m, None)
     n.run(workspace)
     return workspace
Ejemplo n.º 3
0
def get_measurements_for_good_pipeline(nimages = 1, 
                                       group_numbers = None):
    '''Get an appropriately initialized measurements structure for the good pipeline'''
    path = os.path.join(example_images_directory(), "ExampleSBSImages")
    m = cpmeas.Measurements()
    if group_numbers is None:
        group_numbers = [1] * nimages
    group_indexes = [1]
    last_group_number = group_numbers[0]
    group_index = 1
    for group_number in group_numbers:
        if group_number == last_group_number:
            group_index += 1
        else:
            group_index = 1
        group_indexes.append(group_index)
    for i in range(1, nimages+1):
        filename = ("Channel2-%02d-%s-%02d.tif" % 
                    (i, "ABCDEFGH"[int((i-1) / 12)], ((i-1) % 12) + 1))
        url = pathname2url(os.path.join(path, filename))
        m[cpmeas.IMAGE, cpmeas.C_FILE_NAME + "_DNA", i] = filename
        m[cpmeas.IMAGE, cpmeas.C_PATH_NAME + "_DNA", i] = path
        m[cpmeas.IMAGE, cpmeas.C_URL+"_DNA", i] = url
        m[cpmeas.IMAGE, cpmeas.GROUP_NUMBER, i] = group_numbers[i-1]
        m[cpmeas.IMAGE, cpmeas.GROUP_INDEX, i] = group_indexes[i-1]
        jblob = J.run_script("""
        importPackage(Packages.org.cellprofiler.imageset);
        importPackage(Packages.org.cellprofiler.imageset.filter);
        var imageFile=new ImageFile(new java.net.URI(url));
        var imageFileDetails = new ImageFileDetails(imageFile);
        var imageSeries=new ImageSeries(imageFile, 0);
        var imageSeriesDetails = new ImageSeriesDetails(imageSeries, imageFileDetails);
        var imagePlane=new ImagePlane(imageSeries, 0, ImagePlane.ALWAYS_MONOCHROME);
        var ipd = new ImagePlaneDetails(imagePlane, imageSeriesDetails);
        var stack = ImagePlaneDetailsStack.makeMonochromeStack(ipd);
        var stacks = java.util.Collections.singletonList(stack);
        var keys = java.util.Collections.singletonList(imageNumber);
        var imageSet = new ImageSet(stacks, keys);
        imageSet.compress(java.util.Collections.singletonList("DNA"), null);
        """, dict(url=url, imageNumber = str(i)))
        blob = J.get_env().get_byte_array_elements(jblob)
        m[cpmeas.IMAGE, M_IMAGE_SET, i, blob.dtype] = blob
    pipeline = cpp.Pipeline()
    pipeline.loadtxt(StringIO(GOOD_PIPELINE))
    pipeline.write_pipeline_measurement(m)
    return m
        
Ejemplo n.º 4
0
    def test_04_03_load_planes(self):
        file_name = "RLM1 SSN3 300308 008015000.flex"
        maybe_download_test_image(file_name)
        path = testimages_directory()
        pathname = os.path.join(path, file_name)
        url = pathname2url(pathname)
        ftrs = (cpmeas.C_URL, cpmeas.C_SERIES, cpmeas.C_FRAME)
        channels = ("Channel1", "Channel2")
        header = ",".join([
            ",".join(["_".join((ftr, channel)) for ftr in ftrs])
            for channel in channels
        ])

        csv_lines = [header]
        for series in range(4):
            csv_lines.append(",".join([
                '"%s","%d","%d"' % (url, series, frame) for frame in range(2)
            ]))
        csv_text = "\n".join(csv_lines)
        pipeline, module, filename = self.make_pipeline(csv_text)
        assert isinstance(module, L.LoadData)
        m = cpmeas.Measurements()
        image_set_list = cpi.ImageSetList()
        try:
            workspace = cpw.Workspace(pipeline, module, m, None, m,
                                      image_set_list)
            self.assertTrue(module.prepare_run(workspace))
            pixel_hashes = []
            for i in range(4):
                m.next_image_set(i + 1)
                module.run(workspace)
                chashes = []
                for channel in channels:
                    pixel_data = m.get_image(channel).pixel_data
                    h = hashlib.md5()
                    h.update(pixel_data)
                    chashes.append(h.digest())
                self.assertNotEqual(chashes[0], chashes[1])
                for j, ph in enumerate(pixel_hashes):
                    for k, phh in enumerate(ph):
                        for l, phd in enumerate(chashes):
                            self.assertNotEqual(phh, phd)
                pixel_hashes.append(chashes)
        finally:
            os.remove(filename)
Ejemplo n.º 5
0
    def prepare_run(self, workspace):
        m = workspace.measurements
        assert isinstance(m, cpmeas.Measurements)
        root = self.get_base_directory(workspace)

        if m.image_set_count == 0:
            # Oh bad - using LoadSingleImage to load one image and process it
            image_numbers = [1]
        else:
            image_numbers = m.get_image_numbers()

        for image_number in image_numbers:
            dict = self.get_file_names(workspace,
                                       image_set_number=image_number)
            for image_name in dict.keys():
                file_settings = self.get_file_settings(image_name)
                if file_settings.image_objects_choice == IO_IMAGES:
                    #
                    # Add measurements
                    #
                    path_name_category = C_PATH_NAME
                    file_name_category = C_FILE_NAME
                    url_category = C_URL
                else:
                    #
                    # Add measurements
                    #
                    path_name_category = C_OBJECTS_PATH_NAME
                    file_name_category = C_OBJECTS_FILE_NAME
                    url_category = C_OBJECTS_URL

                url = pathname2url(os.path.join(root, dict[image_name]))
                for category, value in (
                    (path_name_category, root),
                    (file_name_category, dict[image_name]),
                    (url_category, url),
                ):
                    measurement_name = "_".join((category, image_name))
                    m.add_measurement(
                        cpmeas.IMAGE,
                        measurement_name,
                        value,
                        image_set_number=image_number,
                    )
        return True
Ejemplo n.º 6
0
 def save_filename_measurements(self, workspace):
     if self.update_file_names.value:
         filename = self.get_filename(workspace,
                                      make_dirs=False,
                                      check_overwrite=False)
         pn, fn = os.path.split(filename)
         url = pathname2url(filename)
         workspace.measurements.add_measurement(cpmeas.IMAGE,
                                                self.file_name_feature,
                                                fn,
                                                can_overwrite=True)
         workspace.measurements.add_measurement(cpmeas.IMAGE,
                                                self.path_name_feature,
                                                pn,
                                                can_overwrite=True)
         workspace.measurements.add_measurement(cpmeas.IMAGE,
                                                self.url_feature,
                                                url,
                                                can_overwrite=True)
Ejemplo n.º 7
0
    def test_04_03_load_planes(self):
        file_name = "RLM1 SSN3 300308 008015000.flex"
        maybe_download_tesst_image(file_name)
        path = testimages_directory()
        pathname = os.path.join(path, file_name)
        url = pathname2url(pathname)
        ftrs = (cpmeas.C_URL, cpmeas.C_SERIES, cpmeas.C_FRAME)
        channels = ("Channel1", "Channel2")
        header = ",".join([",".join(["_".join((ftr, channel)) for ftr in ftrs])
                           for channel in channels])

        csv_lines = [header]
        for series in range(4):
            csv_lines.append(",".join(['"%s","%d","%d"' % (url, series, frame)
                                       for frame in range(2)]))
        csv_text = "\n".join(csv_lines)
        pipeline, module, filename = self.make_pipeline(csv_text)
        assert isinstance(module, L.LoadData)
        m = cpmeas.Measurements()
        image_set_list = cpi.ImageSetList()
        try:
            workspace = cpw.Workspace(pipeline, module, m, None, m,
                                      image_set_list)
            self.assertTrue(module.prepare_run(workspace))
            pixel_hashes = []
            for i in range(4):
                m.next_image_set(i+1)
                module.run(workspace)
                chashes = []
                for channel in channels:
                    pixel_data = m.get_image(channel).pixel_data
                    h = hashlib.md5()
                    h.update(pixel_data)
                    chashes.append(h.digest())
                self.assertNotEqual(chashes[0], chashes[1])
                for j, ph in enumerate(pixel_hashes):
                    for k, phh in enumerate(ph):
                        for l, phd in enumerate(chashes):
                            self.assertNotEqual(phh, phd)
                pixel_hashes.append(chashes)
        finally:
            os.remove(filename)
Ejemplo n.º 8
0
    def prepare_run(self, workspace):
        m = workspace.measurements
        assert isinstance(m, cpmeas.Measurements)
        root = self.get_base_directory(workspace)

        if m.image_set_count == 0:
            # Oh bad - using LoadSingleImage to load one image and process it
            image_numbers = [1]
        else:
            image_numbers = m.get_image_numbers()

        for image_number in image_numbers:
            dict = self.get_file_names(workspace, image_set_number=image_number)
            for image_name in dict.keys():
                file_settings = self.get_file_settings(image_name)
                if file_settings.image_objects_choice == IO_IMAGES:
                    #
                    # Add measurements
                    #
                    path_name_category = C_PATH_NAME
                    file_name_category = C_FILE_NAME
                    url_category = C_URL
                else:
                    #
                    # Add measurements
                    #
                    path_name_category = C_OBJECTS_PATH_NAME
                    file_name_category = C_OBJECTS_FILE_NAME
                    url_category = C_OBJECTS_URL

                url = pathname2url(os.path.join(root, dict[image_name]))
                for category, value in (
                        (path_name_category, root),
                        (file_name_category, dict[image_name]),
                        (url_category, url)):
                    measurement_name = "_".join((category, image_name))
                    m.add_measurement(cpmeas.IMAGE, measurement_name, value,
                                      image_set_number=image_number)
        return True
Ejemplo n.º 9
0
    def run_as_data_tool(self):
        from cellprofiler.gui.editobjectsdlg import EditObjectsDialog
        import wx
        from wx.lib.filebrowsebutton import FileBrowseButton
        from cellprofiler.modules.namesandtypes import ObjectsImageProvider
        from bioformats import load_image

        with wx.Dialog(None) as dlg:
            dlg.Title = "Choose files for editing"
            dlg.Sizer = wx.BoxSizer(wx.VERTICAL)
            sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
            dlg.Sizer.Add(sub_sizer, 0, wx.EXPAND | wx.ALL, 5)
            new_or_existing_rb = wx.RadioBox(dlg,
                                             style=wx.RA_VERTICAL,
                                             choices=("New", "Existing"))
            sub_sizer.Add(new_or_existing_rb, 0, wx.EXPAND)
            objects_file_fbb = FileBrowseButton(
                dlg,
                size=(300, -1),
                fileMask=
                "Objects file (*.tif, *.tiff, *.png, *.bmp, *.jpg)|*.tif;*.tiff;*.png;*.bmp;*.jpg",
                dialogTitle="Select objects file",
                labelText="Objects file:",
            )
            objects_file_fbb.Enable(False)
            sub_sizer.AddSpacer(5)
            sub_sizer.Add(objects_file_fbb, 0, wx.ALIGN_TOP | wx.ALIGN_RIGHT)

            def on_radiobox(event):
                objects_file_fbb.Enable(new_or_existing_rb.GetSelection() == 1)

            new_or_existing_rb.Bind(wx.EVT_RADIOBOX, on_radiobox)

            image_file_fbb = FileBrowseButton(
                dlg,
                size=(300, -1),
                fileMask=
                "Objects file (*.tif, *.tiff, *.png, *.bmp, *.jpg)|*.tif;*.tiff;*.png;*.bmp;*.jpg",
                dialogTitle="Select guide image file",
                labelText="Guide image:",
            )
            dlg.Sizer.Add(image_file_fbb, 0, wx.EXPAND | wx.ALL, 5)

            allow_overlap_checkbox = wx.CheckBox(dlg, -1,
                                                 "Allow objects to overlap")
            allow_overlap_checkbox.Value = True
            dlg.Sizer.Add(allow_overlap_checkbox, 0, wx.EXPAND | wx.ALL, 5)

            buttons = wx.StdDialogButtonSizer()
            dlg.Sizer.Add(buttons, 0,
                          wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.ALL,
                          5)
            buttons.Add(wx.Button(dlg, wx.ID_OK))
            buttons.Add(wx.Button(dlg, wx.ID_CANCEL))
            buttons.Realize()
            dlg.Fit()
            result = dlg.ShowModal()
            if result != wx.ID_OK:
                return
            self.allow_overlap.value = allow_overlap_checkbox.Value
            fullname = objects_file_fbb.GetValue()
            guidename = image_file_fbb.GetValue()

        if new_or_existing_rb.GetSelection() == 1:
            provider = ObjectsImageProvider("InputObjects",
                                            pathname2url(fullname), None, None)
            image = provider.provide_image(None)
            pixel_data = image.pixel_data
            shape = pixel_data.shape[:2]
            labels = [pixel_data[:, :, i] for i in range(pixel_data.shape[2])]
        else:
            labels = None
        #
        # Load the guide image
        #
        guide_image = load_image(guidename)
        if np.min(guide_image) != np.max(guide_image):
            guide_image = (guide_image - np.min(guide_image)) / (
                np.max(guide_image) - np.min(guide_image))
        if labels is None:
            shape = guide_image.shape[:2]
            labels = [np.zeros(shape, int)]
        with EditObjectsDialog(guide_image, labels, self.allow_overlap,
                               self.object_name.value) as dialog_box:
            result = dialog_box.ShowModal()
            if result != wx.OK:
                return
            labels = dialog_box.labels
        n_frames = len(labels)
        with wx.FileDialog(None,
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as dlg:

            dlg.Path = fullname
            dlg.Wildcard = ("Object image file (*.tif,*.tiff)|*.tif;*.tiff|"
                            "Ilastik project file (*.ilp)|*.ilp")
            result = dlg.ShowModal()
            fullname = dlg.Path
            if result == wx.ID_OK:
                if fullname.endswith(".ilp"):
                    self.save_into_ilp(fullname, labels, guidename)
                else:
                    from bioformats.formatwriter import write_image
                    from bioformats.omexml import PT_UINT16

                    if os.path.exists(fullname):
                        os.unlink(fullname)
                    for i, l in enumerate(labels):
                        write_image(fullname,
                                    l,
                                    PT_UINT16,
                                    t=i,
                                    size_t=len(labels))
    def run_as_data_tool(self):
        from cellprofiler.gui.editobjectsdlg import EditObjectsDialog
        import wx
        from wx.lib.filebrowsebutton import FileBrowseButton
        from cellprofiler.modules.namesandtypes import ObjectsImageProvider
        from bioformats import load_image
        
        with wx.Dialog(None) as dlg:
            dlg.Title = "Choose files for editing"
            dlg.Sizer = wx.BoxSizer(wx.VERTICAL)
            box = wx.StaticBox(dlg, -1, "Choose or create new objects file")
            sub_sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
            dlg.Sizer.Add(sub_sizer, 0, wx.EXPAND | wx.ALL, 5)
            new_or_existing_rb = wx.RadioBox(dlg, style=wx.RA_VERTICAL,
                                             choices = ("New", "Existing"))
            sub_sizer.Add(new_or_existing_rb, 0, wx.EXPAND)
            objects_file_fbb = FileBrowseButton(
                dlg, size=(300, -1),
                fileMask="Objects file (*.tif, *.tiff, *.png, *.bmp, *.jpg)|*.tif;*.tiff;*.png;*.bmp;*.jpg",
                dialogTitle="Select objects file",
                labelText="Objects file:")
            objects_file_fbb.Enable(False)
            sub_sizer.AddSpacer(5)
            sub_sizer.Add(objects_file_fbb, 0, wx.ALIGN_TOP | wx.ALIGN_RIGHT)
            def on_radiobox(event):
                objects_file_fbb.Enable(new_or_existing_rb.GetSelection() == 1)
            new_or_existing_rb.Bind(wx.EVT_RADIOBOX, on_radiobox)
            
            image_file_fbb = FileBrowseButton(
                dlg, size=(300, -1),
                fileMask="Objects file (*.tif, *.tiff, *.png, *.bmp, *.jpg)|*.tif;*.tiff;*.png;*.bmp;*.jpg",
                dialogTitle="Select guide image file",
                labelText="Guide image:")
            dlg.Sizer.Add(image_file_fbb, 0, wx.EXPAND | wx.ALL, 5)
            
            allow_overlap_checkbox = wx.CheckBox(dlg, -1, "Allow objects to overlap")
            allow_overlap_checkbox.Value = True
            dlg.Sizer.Add(allow_overlap_checkbox, 0, wx.EXPAND | wx.ALL, 5)
            
            buttons = wx.StdDialogButtonSizer()
            dlg.Sizer.Add(buttons, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.ALL, 5)
            buttons.Add(wx.Button(dlg, wx.ID_OK))
            buttons.Add(wx.Button(dlg, wx.ID_CANCEL))
            buttons.Realize()
            dlg.Fit()
            result = dlg.ShowModal()
            if result != wx.ID_OK:
                return
            self.allow_overlap.value = allow_overlap_checkbox.Value
            fullname = objects_file_fbb.GetValue()
            guidename = image_file_fbb.GetValue()

        if new_or_existing_rb.GetSelection() == 1:
            provider = ObjectsImageProvider(
                "InputObjects",
                pathname2url(fullname),
                None, None)
            image = provider.provide_image(None)
            pixel_data = image.pixel_data
            shape = pixel_data.shape[:2]
            labels = [pixel_data[:, :, i] for i in range(pixel_data.shape[2])]
        else:
            labels = None
        #
        # Load the guide image
        #
        guide_image = load_image(guidename)
        if np.min(guide_image) != np.max(guide_image):
            guide_image = ((guide_image - np.min(guide_image)) / 
                           (np.max(guide_image)  - np.min(guide_image)))
        if labels is None:
            shape = guide_image.shape[:2]
            labels = [np.zeros(shape, int)]
        with EditObjectsDialog(
            guide_image, labels,
            self.allow_overlap, self.object_name.value) as dialog_box:
            result = dialog_box.ShowModal()
            if result != wx.OK:
                return
            labels = dialog_box.labels
        n_frames = len(labels)
        with wx.FileDialog(None,
                           style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as dlg:
        
            dlg.Path = fullname
            dlg.Wildcard = ("Object image file (*.tif,*.tiff)|*.tif;*.tiff|"
                            "Ilastik project file (*.ilp)|*.ilp")
            result = dlg.ShowModal()
            fullname = dlg.Path
            if result == wx.ID_OK:
                if fullname.endswith(".ilp"):
                    self.save_into_ilp(fullname, labels, guidename)
                else:
                    from bioformats.formatwriter import write_image
                    from bioformats.omexml import PT_UINT16
                    if os.path.exists(fullname):
                        os.unlink(fullname)
                    for i, l in enumerate(labels):
                        write_image(fullname, l, PT_UINT16,
                                    t = i, size_t = len(labels))
Ejemplo n.º 11
0
    def run_create_webpage(self, image_paths, thumb_paths=None,
                           metadata=None, alter_fn=None):
        '''Run the create_webpage module, returning the resulting HTML document

        image_paths - list of path / filename tuples. The function will
                      write an image to each of these and put images and
                      measurements into the workspace for each.
        thumb_paths - if present a list of path / filename tuples. Same as above
        metadata    - a dictionary of feature / string values
        alter_fn    - function taking a CreateWebPage module, for you to
                      alter the module's settings
        '''

        np.random.seed(0)
        module = C.CreateWebPage()
        module.module_num = 1
        module.orig_image_name.value = IMAGE_NAME
        module.web_page_file_name.value = DEFAULT_HTML_FILE
        if alter_fn is not None:
            alter_fn(module)
        pipeline = cpp.Pipeline()

        def callback(caller, event):
            self.assertFalse(isinstance(event, cpp.RunExceptionEvent))

        pipeline.add_listener(callback)
        pipeline.add_module(module)

        images = [(IMAGE_NAME, image_paths)]
        if thumb_paths:
            images += [(THUMB_NAME, thumb_paths)]
            self.assertEqual(len(image_paths), len(thumb_paths))
            module.wants_thumbnails.value = True
            module.thumbnail_image_name.value = THUMB_NAME
        else:
            module.wants_thumbnails.value = False

        measurements = cpmeas.Measurements()

        workspace = cpw.Workspace(pipeline, module,
                                  measurements, None, measurements,
                                  None, None)
        for i in range(len(image_paths)):
            image_number = i + 1
            if metadata is not None:
                for key in metadata.keys():
                    values = metadata[key]
                    feature = cpmeas.C_METADATA + "_" + key
                    measurements[cpmeas.IMAGE, feature, image_number] = values[i]

            for image_name, paths in images:
                pixel_data = np.random.uniform(size=(10, 13))
                path_name, file_name = paths[i]
                if path_name is None:
                    path_name = cpprefs.get_default_image_directory()
                    is_file = True
                elif path_name.lower().startswith("http"):
                    is_file = False
                    url = path_name + "/" + file_name
                    if "?" in file_name:
                        file_name = file_name.split("?", 1)[0]
                if is_file:
                    full_path = os.path.abspath(os.path.join(
                            self.directory, path_name, file_name))
                    url = pathname2url(full_path)
                    path = os.path.split(full_path)[0]
                else:
                    full_path = url
                    path = path_name
                if is_file:
                    write_image(full_path, pixel_data, PT_UINT8)
                path_feature = '_'.join((C_PATH_NAME, image_name))
                file_feature = '_'.join((C_FILE_NAME, image_name))
                url_feature = '_'.join((C_URL, image_name))
                measurements[cpmeas.IMAGE, path_feature, image_number] = \
                    path
                measurements[cpmeas.IMAGE, file_feature, image_number] = \
                    file_name
                measurements[cpmeas.IMAGE, url_feature, image_number] = url

        module.post_run(workspace)
        return measurements
    def run_create_webpage(self, image_paths, thumb_paths = None,
                           metadata = None, alter_fn = None):
        '''Run the create_webpage module, returning the resulting HTML document

        image_paths - list of path / filename tuples. The function will
                      write an image to each of these and put images and
                      measurements into the workspace for each.
        thumb_paths - if present a list of path / filename tuples. Same as above
        metadata    - a dictionary of feature / string values
        alter_fn    - function taking a CreateWebPage module, for you to
                      alter the module's settings
        '''

        np.random.seed(0)
        module = C.CreateWebPage()
        module.module_num = 1
        module.orig_image_name.value = IMAGE_NAME
        module.web_page_file_name.value = DEFAULT_HTML_FILE
        if alter_fn is not None:
            alter_fn(module)
        pipeline = cpp.Pipeline()
        def callback(caller, event):
            self.assertFalse(isinstance(event, cpp.RunExceptionEvent))
        pipeline.add_listener(callback)
        pipeline.add_module(module)

        images = [ (IMAGE_NAME, image_paths)]
        if thumb_paths:
            images += [ (THUMB_NAME, thumb_paths)]
            self.assertEqual(len(image_paths), len(thumb_paths))
            module.wants_thumbnails.value = True
            module.thumbnail_image_name.value = THUMB_NAME
        else:
            module.wants_thumbnails.value = False

        measurements = cpmeas.Measurements()

        workspace = cpw.Workspace(pipeline, module,
                                  measurements, None, measurements,
                                  None, None)
        for i in range(len(image_paths)):
            image_number = i+1
            if metadata is not None:
                for key in metadata.keys():
                    values = metadata[key]
                    feature = cpmeas.C_METADATA + "_" + key
                    measurements[cpmeas.IMAGE, feature, image_number] = values[i]

            for image_name, paths in images:
                pixel_data = np.random.uniform(size=(10,13))
                path_name, file_name = paths[i]
                if path_name is None:
                    path_name = cpprefs.get_default_image_directory()
                    is_file = True
                elif path_name.lower().startswith("http"):
                    is_file = False
                    url = path_name + "/" + file_name
                    if "?" in file_name:
                        file_name = file_name.split("?", 1)[0]
                if is_file:
                    full_path = os.path.abspath(os.path.join(
                        self.directory, path_name, file_name))
                    url = pathname2url(full_path)
                    path = os.path.split(full_path)[0]
                else:
                    full_path = url
                    path = path_name
                if is_file:
                    write_image(full_path, pixel_data, PT_UINT8)
                path_feature = '_'.join((C_PATH_NAME, image_name))
                file_feature = '_'.join((C_FILE_NAME, image_name))
                url_feature = '_'.join((C_URL, image_name))
                measurements[cpmeas.IMAGE, path_feature, image_number] = \
                    path
                measurements[cpmeas.IMAGE, file_feature, image_number] =\
                    file_name
                measurements[cpmeas.IMAGE, url_feature, image_number] = url

        module.post_run(workspace)
        return measurements