def mkdir_recursive_if_necessary(directory_name=None, file_name=None):
    """Creates directory if necessary (i.e., doesn't already exist).

    This method checks for the argument `directory_name` first.  If
    `directory_name` is None, this method checks for `file_name` and extracts
    the directory.

    :param directory_name: Path to local directory.
    :param file_name: Path to local file.
    """

    if directory_name is None:
        error_checking.assert_is_string(file_name)
        directory_name = os.path.dirname(file_name)
    else:
        error_checking.assert_is_string(directory_name)

    if directory_name == '':
        return

    try:
        os.makedirs(directory_name)
    except OSError as this_error:
        if this_error.errno == errno.EEXIST and os.path.isdir(directory_name):
            pass
        else:
            raise
def get_image_files(image_dir_or_file_name):
    """Finds image files.

    :param image_dir_or_file_name: See documentation at top of file.
    :return: image_file_names: 1-D list of paths to image files.
    :raises: ValueError: if `image_dir_or_file_name` is a directory and contains
        no files that match the desired format.
    """

    error_checking.assert_is_string(image_dir_or_file_name)
    image_file_names = []

    if os.path.isdir(image_dir_or_file_name):
        file_pattern = '{0:s}/storm=*time=*'.format(image_dir_or_file_name)
        all_file_names = glob.glob(file_pattern)
        all_file_names.sort()

        for this_file_name in all_file_names:
            try:
                check_image_file_name(this_file_name)
                image_file_names.append(this_file_name)
            except:
                warning_string = (
                    'Directory "{0:s}" contains a file ("{1:s}") that does not '
                    'match the desired format ({2:s}).'
                ).format(
                    image_dir_or_file_name, os.path.split(this_file_name)[-1],
                    FILE_FORMAT_STRING
                )

                warnings.warn(warning_string)

        if len(image_file_names) == 0:
            error_string = (
                'Directory "{0:s}" contains no files that match the desired '
                'format ({1:s}).'
            ).format(image_dir_or_file_name, FILE_FORMAT_STRING)

            raise ValueError(error_string)
    else:
        check_image_file_name(image_dir_or_file_name)
        image_file_names = [image_dir_or_file_name]

    return image_file_names
Пример #3
0
def capture_mouse_clicks(image_file_name, instruction_string=''):
    """This interactive method captures coordinates of human mouse clicks.

    N = number of mouse clicks

    :param image_file_name: Path to image file.  This method will display the
        image in a figure window and allow you to click on top.
    :param instruction_string: String with instructions for the user.
    :return: point_objects_pixel_coords: length-N list of points (instances
        of `shapely.geometry.Point`), each containing a click location in pixel
        coordinates.
    :return: num_pixel_rows: Number of pixel rows in the image.
    :return: num_pixel_columns: Number of pixel columns in the image.
    """

    error_checking.assert_file_exists(image_file_name)
    error_checking.assert_is_string(instruction_string)

    image_matrix = Image.open(image_file_name)
    num_pixel_columns, num_pixel_rows = image_matrix.size

    global figure_object
    figure_object = pyplot.subplots(1,
                                    1,
                                    figsize=(FIGURE_WIDTH_INCHES,
                                             FIGURE_HEIGHT_INCHES))[0]

    pyplot.imshow(image_matrix)
    pyplot.title(instruction_string)

    connection_id = figure_object.canvas.mpl_connect('button_press_event',
                                                     _click_handler)
    pyplot.show()
    figure_object.canvas.mpl_disconnect(connection_id)

    point_objects_pixel_coords = [
        shapely.geometry.Point(this_x, this_y)
        for this_x, this_y in zip(x_coords_px, y_coords_px)
    ]

    return point_objects_pixel_coords, num_pixel_rows, num_pixel_columns
Пример #4
0
def write_points(output_file_name,
                 grid_row_by_point,
                 grid_column_by_point,
                 panel_row_by_point,
                 panel_column_by_point,
                 full_storm_id_string=None,
                 storm_time_unix_sec=None):
    """Writes human points of interest for one image to NetCDF file.

    K = number of points of interest

    :param output_file_name: Path to output (NetCDF) file.
    :param grid_row_by_point: length-K numpy array of row indices in data grid
        (floats).
    :param grid_column_by_point: length-K numpy array of column indices in data
        grid (floats).
    :param panel_row_by_point: length-K numpy array of row indices in panel grid
        (integers).
    :param panel_column_by_point: length-K numpy array of column indices in
        panel grid (integers).
    :param full_storm_id_string: See doc for `write_polygons`.
    :param storm_time_unix_sec: Same.
    """

    is_composite = (full_storm_id_string is None
                    and storm_time_unix_sec is None)

    if is_composite:
        full_storm_id_string = DUMMY_STORM_ID_STRING
        storm_time_unix_sec = -1

    error_checking.assert_is_string(full_storm_id_string)
    error_checking.assert_is_integer(storm_time_unix_sec)

    # error_checking.assert_is_integer(num_grid_rows)
    # error_checking.assert_is_greater(num_grid_rows, 0)
    # error_checking.assert_is_integer(num_grid_columns)
    # error_checking.assert_is_greater(num_grid_columns, 0)

    error_checking.assert_is_numpy_array(grid_row_by_point, num_dimensions=1)
    error_checking.assert_is_geq_numpy_array(grid_row_by_point, -0.5)
    # error_checking.assert_is_leq_numpy_array(
    #     grid_row_by_point, num_grid_rows - 0.5)

    num_points = len(grid_row_by_point)
    these_expected_dim = numpy.array([num_points], dtype=int)

    error_checking.assert_is_numpy_array(grid_column_by_point,
                                         exact_dimensions=these_expected_dim)
    error_checking.assert_is_geq_numpy_array(grid_column_by_point, -0.5)
    # error_checking.assert_is_leq_numpy_array(
    #     grid_column_by_point, num_grid_columns - 0.5)

    error_checking.assert_is_numpy_array(panel_row_by_point,
                                         exact_dimensions=these_expected_dim)
    error_checking.assert_is_integer_numpy_array(panel_row_by_point)
    error_checking.assert_is_geq_numpy_array(panel_row_by_point, 0)

    error_checking.assert_is_numpy_array(panel_column_by_point,
                                         exact_dimensions=these_expected_dim)
    error_checking.assert_is_integer_numpy_array(panel_column_by_point)
    error_checking.assert_is_geq_numpy_array(panel_column_by_point, 0)

    file_system_utils.mkdir_recursive_if_necessary(file_name=output_file_name)
    dataset_object = netCDF4.Dataset(output_file_name,
                                     'w',
                                     format='NETCDF3_64BIT_OFFSET')

    dataset_object.setncattr(STORM_ID_KEY, full_storm_id_string)
    dataset_object.setncattr(STORM_TIME_KEY, storm_time_unix_sec)
    dataset_object.createDimension(POINT_DIMENSION_KEY, num_points)

    dataset_object.createVariable(GRID_ROW_BY_POINT_KEY,
                                  datatype=numpy.float32,
                                  dimensions=POINT_DIMENSION_KEY)
    dataset_object.variables[GRID_ROW_BY_POINT_KEY][:] = grid_row_by_point

    dataset_object.createVariable(GRID_COLUMN_BY_POINT_KEY,
                                  datatype=numpy.float32,
                                  dimensions=POINT_DIMENSION_KEY)
    dataset_object.variables[
        GRID_COLUMN_BY_POINT_KEY][:] = grid_column_by_point

    dataset_object.createVariable(PANEL_ROW_BY_POINT_KEY,
                                  datatype=numpy.int32,
                                  dimensions=POINT_DIMENSION_KEY)
    dataset_object.variables[PANEL_ROW_BY_POINT_KEY][:] = panel_row_by_point

    dataset_object.createVariable(PANEL_COLUMN_BY_POINT_KEY,
                                  datatype=numpy.int32,
                                  dimensions=POINT_DIMENSION_KEY)
    dataset_object.variables[
        PANEL_COLUMN_BY_POINT_KEY][:] = panel_column_by_point

    dataset_object.close()
Пример #5
0
def write_polygons(output_file_name,
                   positive_objects_grid_coords,
                   positive_panel_row_by_polygon,
                   positive_panel_column_by_polygon,
                   positive_mask_matrix,
                   negative_objects_grid_coords=None,
                   negative_panel_row_by_polygon=None,
                   negative_panel_column_by_polygon=None,
                   negative_mask_matrix=None,
                   full_storm_id_string=None,
                   storm_time_unix_sec=None):
    """Writes human polygons for one image to NetCDF file.

    P = number of positive regions of interest
    N = number of negative regions of interest

    :param output_file_name: Path to output (NetCDF) file.
    :param positive_objects_grid_coords: length-P list of polygons created by
        `polygons_from_pixel_to_grid_coords`, containing positive regions of
        interest.
    :param positive_panel_row_by_polygon: length-P numpy array of corresponding
        panel rows (non-negative integers).
    :param positive_panel_column_by_polygon: length-P numpy array of
        corresponding panel columns (non-negative integers).
    :param positive_mask_matrix: Binary mask for positive regions of interest,
        created by `polygons_to_mask`.
    :param negative_objects_grid_coords: length-N list of polygons created by
        `polygons_from_pixel_to_grid_coords`, containing negative regions of
        interest.
    :param negative_panel_row_by_polygon: length-N numpy array of corresponding
        panel rows (non-negative integers).
    :param negative_panel_column_by_polygon: length-N numpy array of
        corresponding panel columns (non-negative integers).
    :param negative_mask_matrix: Binary mask for negative regions of interest,
        created by `polygons_to_mask`.
    :param full_storm_id_string: Full storm ID (if polygons were drawn for
        composite, this should be None).
    :param storm_time_unix_sec: Valid time (if polygons were drawn for
        composite, this should be None).
    """

    is_composite = (full_storm_id_string is None
                    and storm_time_unix_sec is None)

    if is_composite:
        full_storm_id_string = DUMMY_STORM_ID_STRING
        storm_time_unix_sec = -1

    error_checking.assert_is_string(full_storm_id_string)
    error_checking.assert_is_integer(storm_time_unix_sec)

    error_checking.assert_is_boolean_numpy_array(positive_mask_matrix)
    error_checking.assert_is_numpy_array(positive_mask_matrix,
                                         num_dimensions=4)

    _check_polygons(polygon_objects_grid_coords=positive_objects_grid_coords,
                    num_panel_rows=positive_mask_matrix.shape[0],
                    num_panel_columns=positive_mask_matrix.shape[1],
                    panel_row_by_polygon=positive_panel_row_by_polygon,
                    panel_column_by_polygon=positive_panel_column_by_polygon)

    if negative_objects_grid_coords is None:
        negative_objects_grid_coords = []
        negative_panel_row_by_polygon = numpy.array([], dtype=int)
        negative_panel_column_by_polygon = numpy.array([], dtype=int)
        negative_mask_matrix = numpy.full(positive_mask_matrix.shape,
                                          False,
                                          dtype=bool)

    error_checking.assert_is_boolean_numpy_array(negative_mask_matrix)
    error_checking.assert_is_numpy_array(negative_mask_matrix,
                                         exact_dimensions=numpy.array(
                                             positive_mask_matrix.shape,
                                             dtype=int))

    _check_polygons(polygon_objects_grid_coords=negative_objects_grid_coords,
                    num_panel_rows=negative_mask_matrix.shape[0],
                    num_panel_columns=negative_mask_matrix.shape[1],
                    panel_row_by_polygon=negative_panel_row_by_polygon,
                    panel_column_by_polygon=negative_panel_column_by_polygon)

    (positive_vertex_rows, positive_vertex_columns,
     these_vertex_to_poly_indices
     ) = _polygon_list_to_vertex_list(positive_objects_grid_coords)

    positive_panel_row_by_vertex = numpy.array([
        positive_panel_row_by_polygon[k] if k >= 0 else numpy.nan
        for k in these_vertex_to_poly_indices
    ])

    positive_panel_column_by_vertex = numpy.array([
        positive_panel_column_by_polygon[k] if k >= 0 else numpy.nan
        for k in these_vertex_to_poly_indices
    ])

    if len(positive_vertex_rows) == 0:
        positive_vertex_rows = numpy.full(1, SENTINEL_VALUE - 1)
        positive_vertex_columns = numpy.full(1, SENTINEL_VALUE - 1)
        positive_panel_row_by_vertex = numpy.full(1, -1, dtype=int)
        positive_panel_column_by_vertex = numpy.full(1, -1, dtype=int)

    (negative_vertex_rows, negative_vertex_columns,
     these_vertex_to_poly_indices
     ) = _polygon_list_to_vertex_list(negative_objects_grid_coords)

    negative_panel_row_by_vertex = numpy.array([
        negative_panel_row_by_polygon[k] if k >= 0 else numpy.nan
        for k in these_vertex_to_poly_indices
    ])

    negative_panel_column_by_vertex = numpy.array([
        negative_panel_column_by_polygon[k] if k >= 0 else numpy.nan
        for k in these_vertex_to_poly_indices
    ])

    file_system_utils.mkdir_recursive_if_necessary(file_name=output_file_name)
    dataset_object = netCDF4.Dataset(output_file_name,
                                     'w',
                                     format='NETCDF3_64BIT_OFFSET')

    dataset_object.setncattr(STORM_ID_KEY, full_storm_id_string)
    dataset_object.setncattr(STORM_TIME_KEY, storm_time_unix_sec)

    dataset_object.createDimension(PANEL_ROW_DIMENSION_KEY,
                                   positive_mask_matrix.shape[0])
    dataset_object.createDimension(PANEL_COLUMN_DIMENSION_KEY,
                                   positive_mask_matrix.shape[1])
    dataset_object.createDimension(GRID_ROW_DIMENSION_KEY,
                                   positive_mask_matrix.shape[2])
    dataset_object.createDimension(GRID_COLUMN_DIMENSION_KEY,
                                   positive_mask_matrix.shape[3])
    dataset_object.createDimension(POSITIVE_VERTEX_DIM_KEY,
                                   len(positive_vertex_rows))
    dataset_object.createDimension(NEGATIVE_VERTEX_DIM_KEY,
                                   len(negative_vertex_rows))

    dataset_object.createVariable(POSITIVE_VERTEX_ROWS_KEY,
                                  datatype=numpy.float32,
                                  dimensions=POSITIVE_VERTEX_DIM_KEY)
    dataset_object.variables[
        POSITIVE_VERTEX_ROWS_KEY][:] = positive_vertex_rows

    dataset_object.createVariable(POSITIVE_VERTEX_COLUMNS_KEY,
                                  datatype=numpy.float32,
                                  dimensions=POSITIVE_VERTEX_DIM_KEY)
    dataset_object.variables[POSITIVE_VERTEX_COLUMNS_KEY][:] = (
        positive_vertex_columns)

    dataset_object.createVariable(POSITIVE_PANEL_ROW_BY_VERTEX_KEY,
                                  datatype=numpy.int32,
                                  dimensions=POSITIVE_VERTEX_DIM_KEY)
    dataset_object.variables[
        POSITIVE_PANEL_ROW_BY_VERTEX_KEY][:] = positive_panel_row_by_vertex

    dataset_object.createVariable(POSITIVE_PANEL_COLUMN_BY_VERTEX_KEY,
                                  datatype=numpy.int32,
                                  dimensions=POSITIVE_VERTEX_DIM_KEY)
    dataset_object.variables[
        POSITIVE_PANEL_COLUMN_BY_VERTEX_KEY][:] = positive_panel_column_by_vertex

    dataset_object.createVariable(NEGATIVE_VERTEX_ROWS_KEY,
                                  datatype=numpy.float32,
                                  dimensions=NEGATIVE_VERTEX_DIM_KEY)
    dataset_object.variables[
        NEGATIVE_VERTEX_ROWS_KEY][:] = negative_vertex_rows

    dataset_object.createVariable(NEGATIVE_VERTEX_COLUMNS_KEY,
                                  datatype=numpy.float32,
                                  dimensions=NEGATIVE_VERTEX_DIM_KEY)
    dataset_object.variables[NEGATIVE_VERTEX_COLUMNS_KEY][:] = (
        negative_vertex_columns)

    dataset_object.createVariable(NEGATIVE_PANEL_ROW_BY_VERTEX_KEY,
                                  datatype=numpy.int32,
                                  dimensions=NEGATIVE_VERTEX_DIM_KEY)
    dataset_object.variables[
        NEGATIVE_PANEL_ROW_BY_VERTEX_KEY][:] = negative_panel_row_by_vertex

    dataset_object.createVariable(NEGATIVE_PANEL_COLUMN_BY_VERTEX_KEY,
                                  datatype=numpy.int32,
                                  dimensions=NEGATIVE_VERTEX_DIM_KEY)
    dataset_object.variables[
        NEGATIVE_PANEL_COLUMN_BY_VERTEX_KEY][:] = negative_panel_column_by_vertex

    dataset_object.createVariable(
        POSITIVE_MASK_MATRIX_KEY,
        datatype=numpy.int32,
        dimensions=(PANEL_ROW_DIMENSION_KEY, PANEL_COLUMN_DIMENSION_KEY,
                    GRID_ROW_DIMENSION_KEY, GRID_COLUMN_DIMENSION_KEY))
    dataset_object.variables[POSITIVE_MASK_MATRIX_KEY][:] = (
        positive_mask_matrix.astype(int))

    dataset_object.createVariable(
        NEGATIVE_MASK_MATRIX_KEY,
        datatype=numpy.int32,
        dimensions=(PANEL_ROW_DIMENSION_KEY, PANEL_COLUMN_DIMENSION_KEY,
                    GRID_ROW_DIMENSION_KEY, GRID_COLUMN_DIMENSION_KEY))
    dataset_object.variables[NEGATIVE_MASK_MATRIX_KEY][:] = (
        negative_mask_matrix.astype(int))

    dataset_object.close()
Пример #6
0
def capture_polygons(image_file_name, instruction_string=''):
    """This interactiv method allows you to draw polygons and captures vertices.

    N = number of polygons drawn

    :param image_file_name: Path to image file.  This method will display the
        image in a figure window and allow you to draw polygons on top.
    :param instruction_string: String with instructions for the user.
    :return: polygon_objects_pixel_coords: length-N list of polygons (instances
        of `shapely.geometry.Polygon`), each containing vertices in pixel
        coordinates.
    :return: num_pixel_rows: Number of pixel rows in the image.
    :return: num_pixel_columns: Number of pixel columns in the image.
    """

    error_checking.assert_file_exists(image_file_name)
    error_checking.assert_is_string(instruction_string)

    image_matrix = Image.open(image_file_name)
    num_pixel_columns, num_pixel_rows = image_matrix.size

    pyplot.imshow(image_matrix)
    pyplot.title(instruction_string)
    pyplot.show(block=False)

    multi_roi_object = MultiRoi()

    string_keys = list(multi_roi_object.rois.keys())
    integer_keys = numpy.array([int(k) for k in string_keys], dtype=int)

    sort_indices = numpy.argsort(integer_keys)
    integer_keys = integer_keys[sort_indices]
    string_keys = [string_keys[k] for k in sort_indices]

    num_polygons = len(integer_keys)
    polygon_objects_pixel_coords = []

    for i in range(num_polygons):
        this_roi_object = multi_roi_object.rois[string_keys[i]]

        these_x_coords = numpy.array([this_roi_object.x[0]] +
                                     list(reversed(this_roi_object.x)))

        if len(these_x_coords) < 4:
            warning_string = (
                'Found polygon with only {0:d} points.  A valid polygon must '
                'have at least 3 points.  Skipping this polygon.'
            ).format(len(these_x_coords) - 1)

            warnings.warn(warning_string)
            continue

        these_y_coords = numpy.array([this_roi_object.y[0]] +
                                     list(reversed(this_roi_object.y)))

        this_polygon_object = polygons.vertex_arrays_to_polygon(
            x_coordinates=these_x_coords, y_coordinates=these_y_coords)

        polygon_objects_pixel_coords.append(this_polygon_object)

    return polygon_objects_pixel_coords, num_pixel_rows, num_pixel_columns
    def test_assert_is_string_true(self):
        """Checks assert_is_string when input is string."""

        error_checking.assert_is_string(SINGLE_STRING)
    def test_assert_is_string_none(self):
        """Checks assert_is_string when input is None."""

        with self.assertRaises(TypeError):
            error_checking.assert_is_string(None)
    def test_assert_is_string_number(self):
        """Checks assert_is_string when input is number."""

        with self.assertRaises(TypeError):
            error_checking.assert_is_string(SINGLE_INTEGER)