Expose public digitize_to_stitches() returning regions + stitch objects without writing a file #75

Closed
opened 2026-06-24 00:11:39 +00:00 by Mark · 0 comments
Owner

Context

Mark/easystitch-service wraps EmbroideryPipeline.process_bytes() to serve an HTTP API. The service contract (see Mark/Kimi_Agent_Brother_10_Digitizing_App#11 and Mark/easystitch-service#1) requires /v1/digitize to return:

{
  "regions": [...],
  "stitchObjects": [...],
  "stats": { "totalStitches": ..., "colorCount": ..., "processingMs": ... }
}

The pipeline currently returns output_bytes (the machine file) but discards the structured regions and stitch points after writing the file. The service has to call a private method (_generate_stitches_from_paths) and re-derive the structured output itself — fragile and leaks implementation.

Goal

Add a public method on EmbroideryPipeline that runs the raster->vector->semantic-annotate->stitch-generate stages and returns the structured result, without invoking the format emitter or writing a file.

Proposed API

class EmbroideryPipeline:
    def digitize_to_stitches(
        self,
        input_bytes: bytes,
        settings: DigitizeSettings,
    ) -> DigitizeResult:
        """Run the pipeline up through stitch generation, returning
        structured regions and stitch objects.

        Does NOT write a machine file. Use process_bytes() / emit()
        for that. Use digitize_to_stitches() when you need the
        structured output (e.g. for an HTTP service that returns JSON).
        """
        ...

@dataclass
class DigitizeSettings:
    fill_density_mm: float = 0.4
    satin_stitch_spacing_mm: float = 0.4
    running_stitch_spacing_mm: float = 0.4
    min_stitch_length_mm: float = 1.0
    max_stitch_length_mm: float = 8.0
    hoop_size_mm: tuple[float, float] = (100.0, 100.0)
    max_colors: int = 10
    machine_profile: MachineProfile = ...
    enable_pull_compensation: bool = True

@dataclass
class DigitizeResult:
    regions: list[Region]
    stitch_objects: list[StitchObject]
    stats: DigitizeStats
    warnings: list[str]
    errors: list[str]
    processing_ms: int

The dataclass shapes can mirror Mark/easystitch-service/models.py (Region, StitchObject, DigitizeStats) so the service translates them 1:1.

Implementation

Refactor _emit_machine_file() in src/main_pipeline.py:

  1. Extract parse -> stitch generation -> pull-compensation into a new private method _build_stitches(svg_content, settings) -> list[StitchObject].
  2. Extract region assembly into _build_regions(stitch_intents) -> list[Region] — derive regions from stitch_intents (polygon + color + stitch_type).
  3. Add the public digitize_to_stitches() that runs stages 1-4, builds regions + stitch objects, returns DigitizeResult.
  4. Update _emit_machine_file() to call _build_stitches() instead of duplicating the logic.

Acceptance

  • New method exists with the proposed signature.
  • Calling digitize_to_stitches(png_bytes, settings) on a 1024x1024 PNG returns DigitizeResult with non-empty regions and stitch_objects.
  • No file is written to disk.
  • Unit test in tests/test_main.py covers the new method.
  • The existing process_bytes() API is unchanged.

Blocks

  • Mark/easystitch-service#1 (FastAPI wrapper) — can't return the structured shape until this lands.
  • Mark/Kimi_Agent_Brother_10_Digitizing_App#6, #8, #9, #10, #2 — all need /v1/digitize to return regions + stitch objects.

Notes

  • _emit_machine_file() currently branches on from_raster and uses a placeholder stitch generator for raster inputs. Separate issue #3 (raster path uses placeholder stitches) tracks that.
  • Settings dict currently hardcoded inside _emit_machine_file() — separate issue #2 (thread settings through pipeline) tracks that.
## Context `Mark/easystitch-service` wraps `EmbroideryPipeline.process_bytes()` to serve an HTTP API. The service contract (see `Mark/Kimi_Agent_Brother_10_Digitizing_App#11` and `Mark/easystitch-service#1`) requires `/v1/digitize` to return: ```json { "regions": [...], "stitchObjects": [...], "stats": { "totalStitches": ..., "colorCount": ..., "processingMs": ... } } ``` The pipeline currently returns `output_bytes` (the machine file) but **discards the structured regions and stitch points** after writing the file. The service has to call a private method (`_generate_stitches_from_paths`) and re-derive the structured output itself — fragile and leaks implementation. ## Goal Add a public method on `EmbroideryPipeline` that runs the raster->vector->semantic-annotate->stitch-generate stages and returns the structured result, **without** invoking the format emitter or writing a file. ## Proposed API ```python class EmbroideryPipeline: def digitize_to_stitches( self, input_bytes: bytes, settings: DigitizeSettings, ) -> DigitizeResult: """Run the pipeline up through stitch generation, returning structured regions and stitch objects. Does NOT write a machine file. Use process_bytes() / emit() for that. Use digitize_to_stitches() when you need the structured output (e.g. for an HTTP service that returns JSON). """ ... @dataclass class DigitizeSettings: fill_density_mm: float = 0.4 satin_stitch_spacing_mm: float = 0.4 running_stitch_spacing_mm: float = 0.4 min_stitch_length_mm: float = 1.0 max_stitch_length_mm: float = 8.0 hoop_size_mm: tuple[float, float] = (100.0, 100.0) max_colors: int = 10 machine_profile: MachineProfile = ... enable_pull_compensation: bool = True @dataclass class DigitizeResult: regions: list[Region] stitch_objects: list[StitchObject] stats: DigitizeStats warnings: list[str] errors: list[str] processing_ms: int ``` The dataclass shapes can mirror `Mark/easystitch-service/models.py` (Region, StitchObject, DigitizeStats) so the service translates them 1:1. ## Implementation Refactor `_emit_machine_file()` in `src/main_pipeline.py`: 1. Extract parse -> stitch generation -> pull-compensation into a new private method `_build_stitches(svg_content, settings) -> list[StitchObject]`. 2. Extract region assembly into `_build_regions(stitch_intents) -> list[Region]` — derive regions from `stitch_intents` (polygon + color + stitch_type). 3. Add the public `digitize_to_stitches()` that runs stages 1-4, builds regions + stitch objects, returns `DigitizeResult`. 4. Update `_emit_machine_file()` to call `_build_stitches()` instead of duplicating the logic. ## Acceptance - New method exists with the proposed signature. - Calling `digitize_to_stitches(png_bytes, settings)` on a 1024x1024 PNG returns `DigitizeResult` with non-empty `regions` and `stitch_objects`. - No file is written to disk. - Unit test in `tests/test_main.py` covers the new method. - The existing `process_bytes()` API is unchanged. ## Blocks - `Mark/easystitch-service#1` (FastAPI wrapper) — can't return the structured shape until this lands. - `Mark/Kimi_Agent_Brother_10_Digitizing_App#6, #8, #9, #10, #2` — all need `/v1/digitize` to return regions + stitch objects. ## Notes - `_emit_machine_file()` currently branches on `from_raster` and uses a placeholder stitch generator for raster inputs. Separate issue #3 (raster path uses placeholder stitches) tracks that. - Settings dict currently hardcoded inside `_emit_machine_file()` — separate issue #2 (thread settings through pipeline) tracks that.
Mark 2026-06-24 00:11:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Mark/easystitch#75
No description provided.