stringart package

Submodules

stringart.animate module

stringart.animate.animate_stringart()

TODO: implement animation function

stringart.canvas module

stringart.canvas.create_canvas(shape: tuple[int] | list[int])

Create an empty white canvas. The empty canvas is returned as a numpy array of ones with the provided shape.

Parameters:

shape (tuple of int or list of int) – Shape of the empty white canvas (array of ones) to be returned.

Returns:

canvas – Array of ones of provided shape.

Return type:

array

stringart.canvas.create_canvas_and_nail_positions(shape: tuple[int] | list[int], nail_layout: str, num_nails: int)

Create an empty white canvas of provided shape and nails positions array of provided number and geometry.

Parameters:
  • shape (tuple of int or list of int) – Shape of the canvas around which to place nails.

  • nail_layout ({"circle", "rectangle"}) – Geometric layout of the nails; must be either “circle” or “rectangle”.

  • num_nails (int) – Number of nails to place around the edge of the canvas, linearly spaced around a rectangle or a circle.

Returns:

  • canvas (array) – Array of ones of provided shape.

  • nail_positions (array) – Array of shape (num_nails, 2), containing the num_nails nail positions in the (row, column) coordinate system.

  • nail_angles (array) – Array of shape (num_nails, ) of num_nails linearly spaced nail angles between 0 and 2π (0 and 2π are not counted twice). This is None if nail_layout==”rectangle”.

stringart.canvas.create_circle_nail_positions(shape: tuple[int] | list[int], num_nails: int)

Create an array containing the positions of nails placed along a circle on the edge of the given canvas.

Parameters:
  • shape (tuple of int or list of int) – Shape of the canvas array around which to place nails.

  • num_nails (int) – Number of nails to place around the edge of the canvas, linearly spaced around a circle.

Returns:

  • nail_positions (array) – Array of shape (num_nails, 2), containing the num_nails nail positions in the (row, column) coordinate system.

  • nail_angles (array) – Array of shape (num_nails, ) of num_nails linearly spaced nail angles between 0 and 2π (0 and 2π are not counted twice).

stringart.canvas.create_rectangle_nail_positions(shape: tuple[int] | list[int], num_nails: int)

Create an array containing the positions of nails placed along a rectangle on the edge of the given canvas.

Parameters:
  • shape (tuple of int or list of int) – Shape of the canvas array around which to place nails.

  • num_nails (int) – Number of nails to place around the edge of the canvas, linearly spaced around a rectangle.

Returns:

nail_positions – Array of shape (num_nails, 2), containing the num_nails nail positions in the (row, column) coordinate system. Each side of the rectangle contains a fraction of num_nails proportional to the length of that side compared to the perimeter.

Return type:

array

stringart.cli module

stringart.cli.stringart_cli(argv=None)

stringart.demo module

stringart.demo.get_demo_image_path(filename: str = 'einstein.jpg')

Get the path of one of the available stringart.data.demo_img to be opened using stringart.image_io.

Parameters:

filename (str) – Name of the files output by stringart.demo.list_demo_images().

Returns:

filepath – Path of the chosen demo image file.

Return type:

path

stringart.demo.list_demo_images()

List file names of available demo images contained in stringart.data.demo_img.

Returns:

demo_images_names_list – List containing the file names of the available demo images.

Return type:

list of str

stringart.demo.list_demo_images_paths()

List paths of available demo images contained in stringart.data.demo_img.

Returns:

demo_images_path_list – List containing the paths of the available demo images.

Return type:

list of path

stringart.image_io module

stringart.image_io.from_string_idx_order_to_image_array(string_idx_order: ndarray, shape: tuple[int], nail_layout: str, num_nails: int, string_strength: float)

Reconstruct a canvas image from a sequence of nail indices.

Parameters:
  • string_idx_order (array of int) – Sequence of nail indices representing the order in which strings are drawn.

  • shape (tuple of int) – Shape of the canvas (height, width).

  • nail_layout ({"circle", "rectangle"}) – Layout of nails.

  • num_nails (int) – Total number of nails on the canvas.

  • string_strength (float) – Strength of the string; controls how much each line darkens the canvas.

Returns:

canvas – 2D array of floats in [0, 1] representing the reconstructed string art image.

Return type:

array

stringart.image_io.open_grayscale_crop_fixbg_img(img_path: str | Path, background_color: tuple[int] | list[int], nail_layout: str)

Open an image, convert it to grayscale, fix transparent background, and crop for circular layouts.

Parameters:
  • img_path (str or pathlib.Path) – Path to the image file.

  • background_color (tuple of int or list of int) – RGB color to replace transparent background if present.

  • nail_layout ({"circle", "rectangle"}) – Layout of nails; if “circle”, the image is center-cropped to a square.

Returns:

img – 2D grayscale image array, optionally cropped (if nail_layout==”circle”) and with transparent background replaced (if originally present).

Return type:

array

stringart.image_io.open_image(img_path: str | Path)

Load an image from a file path as a numpy array.

Parameters:

img_path (str or pathlib.Path) – Path to the image file.

Returns:

img – Image as a numpy array of shape (H, W) for grayscale or (H, W, C) for RGB/RGBA.

Return type:

array

stringart.image_io.replace_transparent_background(img: ndarray, background_color: tuple[int] | list[int] = (50, 50, 50))

Replace the transparent background in a PNG image with a solid color.

Parameters:
  • img (array) – Input image array with shape (H, W, 4), including an alpha channel.

  • background_color (tuple of int or list of int, default (50, 50, 50)) – RGB color to replace transparent regions. Values must be in [0, 255].

Returns:

composited_img – Image array with shape (H, W, 3), where the transparent background is replaced by background_color.

Return type:

array

stringart.image_io.resolve_output_path(user_path: str, default_name: str, allowed_exts: str | list[str] | None = None)

Resolves the path where to save a file, allowing multiple extensions.

Parameters:
  • user_path (str) – Path provided by the user. Can be a folder or a file.

  • default_name (str) – Default filename to use if user_path is a folder.

  • allowed_exts (list[str], optional) – List of allowed file extensions (with leading dot, e.g., [‘.png’, ‘.jpg’]).

Returns:

Resolved full path to the file.

Return type:

pathlib.Path

stringart.image_io.save_stringart(canvas: ndarray, path: str)

Save a string art canvas to disk as an image or PDF.

Parameters:
  • canvas (array) – 2D array of floats in [0, 1] representing the canvas.

  • path (str) – File path to save the image. Supported extensions: .png, .jpg, .pdf.

stringart.pathfinding module

stringart.pathfinding.cache_numpy(func: callable)

Cache a function with numpy.ndarray arguments.

stringart.pathfinding.get_aa_line_coordinates_with_cache(from_idx: int, to_idx: int, nail_positions: ndarray)

Get anti-aliased line pixel coordinates and values. Lines are cached as they are computed.

Parameters:
  • from_idx (int) – Integer index of starting position nail from nail_positions.

  • to_idx (int) – Integer index of ending position nail from nail_positions.

  • nail_positions (array) – Array of shape (num_nails, 2), containing the nails positions in the (row, column) coordinate system.

Returns:

rr, cc, val – Indices of pixels (rr, cc) and intensity values (val). img[rr, cc] = val.

Return type:

(N,) ndarray (int, int, float)

See also

skimage.draw.line_aa

function used to compute the anti-aliased line pixel coordinates/values.

stringart.pathfinding.get_aa_line_no_cache(from_idx: int, to_idx: int, nail_positions: ndarray, string_strength: float, picture: ndarray)

Get an anti-aliased line from from_idx to to_idx in nail_positions and draw it on picture without caching.

Parameters:
  • from_idx (int) – Integer index of the starting nail in nail_positions.

  • to_idx (int) – Integer index of the ending nail in nail_positions.

  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

  • string_strength (float) – Scalar strength of the string; controls how much the line darkens the canvas.

  • picture (array) – 2D array representing the current canvas or working image. Values are in [0, 1].

Returns:

  • line (array) – 1D array of updated pixel values along the line.

  • rr (array) – Row indices of the pixels along the line.

  • cc (array) – Column indices of the pixels along the line.

  • val (array) – Anti-aliased intensity values for each pixel along the line.

stringart.pathfinding.get_aa_line_with_cache(from_idx: int, to_idx: int, nail_positions: ndarray, string_strength: float, picture: ndarray)

Get an anti-aliased line from from_idx to to_idx in nail_positions and draw it on the provided picture.

The line is computed with anti-aliasing using skimage.draw.line_aa. The resulting pixel values are subtracted from the provided picture multiplied by string_strength (to darken/thicken the line). Lines are cached for efficiency.

Parameters:
  • from_idx (int) – Integer index of the starting nail in nail_positions.

  • to_idx (int) – Integer index of the ending nail in nail_positions.

  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

  • string_strength (float) – Scalar strength (“thickness”) of the string; controls how much the line darkens the canvas (0 = no effect, 1 = fully black).

  • picture (array) – 2D array of shape (height, width) representing the current canvas or working image. Values are in [0, 1].

Returns:

  • line (array) – 1D array of same length as the number of pixels along the line; represents the updated pixel values along the line.

  • rr (array) – 1D array of row indices of the pixels along the line.

  • cc (array) – 1D array of column indices of the pixels along the line.

  • val (array) – 1D array of anti-aliased intensity values for each pixel in the line (in [0, 1]).

stringart.pathfinding.get_next_nail_position(current_nail_idx: int, canvas: ndarray, nail_positions: ndarray, original_img: ndarray, string_strength: float, should_skip_function: callable, cache_lines: bool = True)

Find the next nail that best improves the string art approximation.

Evaluates all possible candidate nails (skipping invalid ones based on layout rules) and selects the nail that maximizes the reduction in L2 error between the current canvas and the target original_img.

Parameters:
  • current_nail_idx (int) – Index of the current nail.

  • canvas (array) – 2D array representing the current canvas state.

  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

  • original_img (array) – 2D array of the target image to approximate.

  • string_strength (float) – Scalar strength of the string; determines how much the line darkens the canvas.

  • should_skip_function (callable) – Function used to determine whether to skip a pair of nails.

  • cache_lines (bool, default True) – Whether to cache computed lines for performance.

Returns:

  • best_nail_idx (int or None) – Index of the next nail that gives the best improvement. None if no valid move exists.

  • new_canvas (array or None) – Updated canvas after drawing the line to best_nail_idx. None if no valid move exists.

  • distance (float or None) – Mean squared error between the updated canvas and the original image. None if no valid move exists.

  • best_improvement (float) – Improvement in squared error achieved by this move.

stringart.pathfinding.get_optimal_string_path(canvas: ndarray, nail_positions: ndarray, original_img: ndarray, string_strength: float, max_num_iter: int, nail_layout: str, nail_angles: ndarray | None = None, min_angle_diff: float = 0.39269908169872414, cache_lines: bool = True, patience: int = 20, epsilon: float = 1e-06)

Compute an optimal sequence of nails to approximate an image with string art.

Iteratively selects the next nail to draw a string to, updating the canvas and tracking the MSE. Stops early if no improvement is seen for patience consecutive iterations.

Parameters:
  • canvas (array) – Initial canvas (white background) as a 2D array.

  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

  • original_img (array) – 2D array representing the target image to approximate.

  • string_strength (float) – Scalar strength of the string; determines how much the line darkens the canvas.

  • max_num_iter (int) – Maximum number of strings/iterations to perform.

  • nail_layout ({"circle", "rectangle"}) – Layout of the nails; affects candidate skipping rules.

  • nail_angles (array, optional) – Array of nail angles in radians, required for circular layouts.

  • min_angle_diff (float, default np.pi / 8) – Minimum allowed angular difference between consecutive lines (for circular layouts).

  • cache_lines (bool, default True) – Whether to cache computed lines for performance.

  • patience (int, default 20) – Number of consecutive iterations with negligible improvement before early stopping.

  • epsilon (float, default 1e-6) – Threshold for determining negligible improvement.

Returns:

  • string_idx_order (array) – Array of indices of nails in the order they were selected.

  • canvas (array) – Final canvas after all iterations.

  • distance_vec (array) – Array of mean squared errors between the canvas and the original image after each iteration.

stringart.pathfinding_precache module

stringart.pathfinding_precache.get_aa_line_from_precache(from_idx: int, to_idx: int, picture: ndarray, string_strength: float, line_cache_dict: dict)

Retrieve a line from the precomputed cache and draw it on the current picture.

Parameters:
  • from_idx (int) – Index of the starting nail.

  • to_idx (int) – Index of the ending nail.

  • picture (array) – 2D array representing the current canvas or working image.

  • string_strength (float) – Scalar strength of the string; controls how much the line darkens the canvas.

  • line_cache_dict (dict) – Precomputed line cache from precache_lines.

Returns:

  • line (array or None) – Updated pixel values along the line. None if the line is not in the cache.

  • rr (array or None) – Row indices of pixels along the line. None if not cached.

  • cc (array or None) – Column indices of pixels along the line. None if not cached.

  • val (array or None) – Anti-aliased intensity values for the line. None if not cached.

stringart.pathfinding_precache.get_next_nail_position_precache(current_nail_idx: int, canvas: ndarray, nail_positions: ndarray, original_img: ndarray, string_strength: float, line_cache_dict: dict)

Select the next nail that maximally reduces the MSE using precomputed lines.

Parameters:
  • current_nail_idx (int) – Index of the current nail.

  • canvas (array) – 2D array representing the current canvas state.

  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

  • original_img (array) – 2D array representing the target image to approximate.

  • string_strength (float) – Strength of the string; controls how much the line darkens the canvas.

  • line_cache_dict (dict) – Dictionary of precomputed lines from precache_lines.

Returns:

  • best_nail_idx (int or None) – Index of the next nail that gives the best improvement. None if no valid move exists.

  • new_canvas (array or None) – Updated canvas after drawing the line. None if no valid move exists.

  • distance (float or None) – Mean squared error between the updated canvas and the original image. None if no valid move exists.

  • best_improvement (float) – Reduction in squared error achieved by this move.

stringart.pathfinding_precache.get_optimal_string_path_precache(canvas: ndarray, nail_positions: ndarray, original_img: ndarray, string_strength: float, max_num_iter: int, nail_layout: str, nail_angles: ndarray | None = None, min_angle_diff: float = 0.39269908169872414, patience: int = 20, epsilon: float = 1e-06, line_cache_dict: dict | None = None)

Compute an optimal sequence of nails using precomputed line cache for efficiency.

Parameters:
  • canvas (array) – Initial canvas (white background) as a 2D array.

  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

  • original_img (array) – 2D array representing the target image to approximate.

  • string_strength (float) – Scalar strength of the string; controls how much each line darkens the canvas.

  • max_num_iter (int) – Maximum number of strings/iterations to perform.

  • nail_layout ({"circle", "rectangle"}) – Layout of the nails; affects candidate skipping rules.

  • nail_angles (array, optional) – Array of nail angles in radians, required for circular layouts.

  • min_angle_diff (float, default np.pi / 8) – Minimum allowed angular difference between consecutive lines (for circular layouts).

  • patience (int, default 20) – Number of consecutive iterations with negligible improvement before early stopping.

  • epsilon (float, default 1e-6) – Threshold for determining negligible improvement.

  • line_cache_dict (dict, default None) – Dictionary of precomputed lines from precache_lines; if empty, it will be created using precache_lines. Use this argument if the cache dict has been computed separately beforehand.

Returns:

  • string_idx_order (array) – Array of indices of nails in the order they were selected.

  • canvas (array) – Final canvas after all iterations.

  • distance_vec (array) – Array of mean squared errors between the canvas and the original image after each iteration.

stringart.pathfinding_precache.precache_lines(nail_positions: ndarray, string_strength: float, canvas_shape: tuple[int], should_skip: callable)

Precompute and cache anti-aliased line coordinates for all valid nail pairs.

Parameters:
  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

  • string_strength (float) – Strength of the string; used to compute pixel intensity values along lines.

  • canvas_shape (tuple of int) – Shape of the canvas on which lines will be drawn.

  • should_skip (callable) – Function that returns True for nail pairs that should be skipped (e.g., invalid connections).

Returns:

line_cache_dict – Dictionary mapping sorted nail index pairs (i, j) to a tuple (rr, cc, val), where rr and cc are pixel coordinates along the line and val are anti-aliased intensity values.

Return type:

dict

stringart.skip module

stringart.skip.get_should_skip_function(nail_layout: str, positions_or_angles: ndarray | None = None, min_angle_diff: float = 0.39269908169872414)

Return a function that determines whether a nail pair should be skipped based on layout.

For rectangle layouts, returns a function that skips nails on the same side. For circle layouts, returns a function that skips nails that are too close in angle.

Parameters:
  • nail_layout ({"circle", "rectangle"}) – Layout of the nails.

  • positions_or_angles (array, optional) – For rectangle: (num_nails, 2) nail positions. For circle: (num_nails,) nail angles in radians.

  • min_angle_diff (float, default π/8) – Minimum angular difference for circle layouts. Ignored for rectangle layouts.

Returns:

should_skip – Function should_skip(i, j) returning True if the nail pair (i, j) should be skipped.

Return type:

callable

stringart.skip.should_skip_circle(from_idx: int, to_idx: int, nail_angles: ndarray, min_angle_diff: float = 0.39269908169872414)

Determine whether a pair of nails on a circle should be skipped due to being too close.

Parameters:
  • from_idx (int) – Index of the first nail in the pair.

  • to_idx (int) – Index of the second nail in the pair.

  • nail_angles (array) – Array of shape (num_nails,) containing nail angles in radians (0 to 2π, 0 and 2π not repeated).

  • min_angle_diff (float, default π/8) – Minimum allowed angular distance between consecutive nails.

Returns:

should_skip – True if the angular distance between the nails is less than min_angle_diff; False otherwise.

Return type:

bool

stringart.skip.should_skip_rectangle(from_idx: int, to_idx: int, nail_positions: ndarray)

Determine whether a pair of nails on a rectangle should be skipped.

Nails are skipped if they lie on the same side of the rectangle or are the same nail.

Parameters:
  • from_idx (int) – Index of the first nail in the pair.

  • to_idx (int) – Index of the second nail in the pair.

  • nail_positions (array) – Array of shape (num_nails, 2) containing nail positions in (row, column) coordinates.

Returns:

should_skip – True if the nails are on the same side or identical; False otherwise.

Return type:

bool

stringart.transforms module

stringart.transforms.center_square_crop(img: ndarray)

Crop a 2D image to a centered square.

If the input image is rectangular, this function crops the largest possible square from the center of the image.

Parameters:

img (array) – 2D image array of shape (height, width).

Returns:

cropped_img – Center-cropped square image of shape (side, side), where side = min(height, width).

Return type:

array

stringart.transforms.downscale(img: ndarray, downscale_factor: float)

Downscale an image by a specified factor using anti-aliasing.

Parameters:
  • img (array) – Input image (2D grayscale or 3D RGB).

  • downscale_factor (float) – Factor to downscale the image. Must be >0 and <=1. Values <1 shrink the image; values >1 or <=0 are invalid. Value 1 does no downscaling.

Returns:

downscaled_img – Downscaled image, same number of channels as the input.

Return type:

array

Raises:

ValueError – If downscale_factor is <=0 or >1.

stringart.transforms.rgb2gray(img: ndarray)

Convert an RGB image to grayscale.

Uses skimage.color.rgb2gray internally.

Parameters:

img (array) – 2D grayscale image or 3D RGB image array of shape (height, width, 3).

Returns:

gray_img – 2D grayscale image with values in [0, 1].

Return type:

array

Module contents

stringart.animate_stringart()

TODO: implement animation function

stringart.create_stringart(img_path: str | Path, num_nails: int, downscale_factor: float, string_strength: float = 0.1, max_num_iter: int = 5000, nail_layout: str = 'circle', cache_lines: bool = True, precache_lines: bool = True, min_angle_diff=0.39269908169872414, background_color: tuple[int] | list[int] = (50, 50, 50), patience: int = 20, epsilon: float = 1e-06)

Generate string art from an input image by computing the optimal nail sequence.

The function converts the input image to grayscale, optionally downsamples it for faster computation, creates a virtual canvas with nails in a circle or rectangle layout, and iteratively selects the sequence of nails that best approximates the image with dark string lines. Lines can be cached/precached to speed up the computation. The loop stops if no improvement greater than epsilon is observed for patience consecutive iterations.

Parameters:
  • img_path (str or path) – Path to the input image. Transparent backgrounds are replaced with background_color.

  • num_nails (int) – Number of nails to place around the edge of the canvas.

  • downscale_factor (float) – Factor to downscale the input image for faster computation.

  • string_strength (float, default 0.1) – Intensity of each string line; controls how much it darkens the canvas (0 = no effect, 1 = fully black).

  • max_num_iter (int, default 5000) – Maximum number of strings/iterations to draw.

  • nail_layout ({"circle", "rectangle"}, default "circle") – Layout of nails on the canvas.

  • cache_lines (bool, default True) – Whether to cache line computations for efficiency.

  • precache_lines (bool, default True) – Whether to precache all possible lines before starting if cache_lines is True.

  • min_angle_diff (float, default π/8) – Minimum allowed angular difference between consecutive lines for circular layouts.

  • background_color (tuple of int, default (50, 50, 50)) – RGB color used to replace transparent backgrounds in input images.

  • patience (int, default 20) – Number of consecutive iterations with negligible improvement before early stopping.

  • epsilon (float, default 1e-6) – Threshold for determining negligible improvement in mean squared error.

Returns:

  • string_idx_order (array of int) – Sequence of nail indices selected to approximate the image.

  • canvas (array of float) – Final 2D canvas with string art drawn. Values are in [0, 1].

  • distance_vec (array of float) – Array of mean squared errors between the canvas and the original image after each iteration.