Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"editor.formatOnSave": true,
"editor.tabSize": 2,
"python.analysis.typeCheckingMode": "standard",
"python.formatting.blackArgs": [
"python.formatting.blackArgs": [do
"--line-length=88"
],
"python.formatting.provider": "black"
Expand All @@ -41,7 +41,8 @@
"mounts": [
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached"
],
"postCreateCommand": "sudo apt-get install -y inotify-tools && (grep -q 'IgnoreUnknown UseKeychain' ~/.ssh/config 2>/dev/null || sed -i '/UseKeychain/i IgnoreUnknown UseKeychain' ~/.ssh/config 2>/dev/null) && git config core.editor '/usr/bin/vim' && git config pull.rebase false && sudo curl -fsSL https://gist.githubusercontent.com/pmalacho-mit/d64caa8e16b0b0fdd5e58cc37a0ce242/raw/8f0f620d4a65f6f3d930ea14e7c2fdf382ef1bf1/codeblockify.sh -o /usr/local/bin/codeblockify && sudo chmod +x /usr/local/bin/codeblockify && curl -fsSL https://raw.githubusercontent.com/pmalacho-mit/devcontainers/refs/heads/main/src/difftastic/install.sh | sudo install -m 0755 /dev/stdin /usr/local/bin/difftastic && git config diff.external difftastic",
"postCreateCommand": "sudo apt-get install -y inotify-tools && sudo apt-get update && sudo apt-get install -y libasound-dev libportaudio2 libportaudiocpp0 portaudio19-dev && sudo apt-get install -y gcc && (grep -q 'IgnoreUnknown UseKeychain' ~/.ssh/config 2>/dev/null || sed -i '/UseKeychain/i IgnoreUnknown UseKeychain' ~/.ssh/config 2>/dev/null) && git config core.editor '/usr/bin/vim' && git config pull.rebase false && sudo curl -fsSL https://gist.githubusercontent.com/pmalacho-mit/d64caa8e16b0b0fdd5e58cc37a0ce242/raw/8f0f620d4a65f6f3d930ea14e7c2fdf382ef1bf1/codeblockify.sh -o /usr/local/bin/codeblockify && sudo chmod +x /usr/local/bin/codeblockify && curl -fsSL https://raw.githubusercontent.com/pmalacho-mit/devcontainers/refs/heads/main/src/difftastic/install.sh | sudo install -m 0755 /dev/stdin /usr/local/bin/difftastic && git config diff.external difftastic",
"workspaceFolder": "${localWorkspaceFolder}",
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind"
}
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
"postStartCommand": "pip install --upgrade -r requirements.txt"
}
5 changes: 5 additions & 0 deletions arc_line_vectorization_suede/.dependencies/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
networkx==3.6.1
numpy==2.4.4
pillow==12.2.0
scikit-image==0.26.0
scipy==1.17.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: subrepo-pull-into-main
on:
push:
branches: [release]

permissions:
contents: write
pull-requests: write

jobs:
pull-to-main:
if: ${{ github.run_number > 1 }}
runs-on: ubuntu-latest
steps:
- name: Set up Git user
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"

- name: Install git-subrepo
run: |
git clone https://github.com/ingydotnet/git-subrepo ~/.git-subrepo
source ~/.git-subrepo/.rc
git subrepo --version

- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: release

- name: Revert (non-merge) commits from this push
id: revert-release
run: |
set -e

# Commit at the tip of release when this workflow runs (i.e., AFTER)
TRIGGER_COMMIT=$(git rev-parse HEAD)
echo "Trigger commit: $TRIGGER_COMMIT"
echo "trigger-commit=$TRIGGER_COMMIT" >> "$GITHUB_OUTPUT"

# Get BEFORE and AFTER from the push event
BEFORE="${{ github.event.before }}"
AFTER="${{ github.sha }}"
echo "Before: $BEFORE"
echo "After: $AFTER"

# List non-merge commits introduced by this push (newest-first)
COMMITS=$(git rev-list --no-merges "$BEFORE..$AFTER" || true)

if [ -z "$COMMITS" ]; then
echo "No non-merge commits to revert between $BEFORE and $AFTER."
fi

echo "Non-merge commits found:"
echo "$COMMITS"

# ---- Collect all commit messages (newest → oldest) ----
ALL_MSGS=""
for c in $COMMITS; do
MSG=$(git log -1 --format=%B "$c")
ALL_MSGS="$ALL_MSGS

---
Commit: $c
$MSG
"
done

echo "all-commit-messages<<EOF" >> "$GITHUB_OUTPUT"
echo "$ALL_MSGS" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"

# ---- Revert the commits (newest-first) ----
for c in $COMMITS; do
echo "Reverting $c"
git revert --no-edit "$c"
done

# Capture the commit that represents the final revert state
REVERT_COMMIT=$(git rev-parse HEAD)
echo "revert-commit=$REVERT_COMMIT" >> "$GITHUB_OUTPUT"

# Push the revert(s) back to release
git push origin release

- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main

- name: Check for release/ folder
run: |
if [ ! -d "release" ]; then
echo "release/ folder not found on main branch. Exiting early."
exit 0
fi

- name: Update ./release to latest
run: |
set -e
source ~/.git-subrepo/.rc
git subrepo pull ./release

- name: Capture .gitrepo content
id: capture-gitrepo
run: |
set -e
# Save the current .gitrepo content
GITREPO_CONTENT=$(cat ./release/.gitrepo)
echo "gitrepo-content<<EOF" >> $GITHUB_OUTPUT
echo "$GITREPO_CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Determine repository owner and name
id: repo-info
run: |
set -e
OWNER="${{ github.repository_owner }}"
NAME="${{ github.event.repository.name }}"
echo "owner=$OWNER" >> $GITHUB_OUTPUT
echo "name=$NAME" >> $GITHUB_OUTPUT
echo "Repository: $OWNER/$NAME"

- name: Delete release folder
run: |
rm -rf ./release
echo "Deleted release/ folder"

- name: Install trigger commit using degit.sh
run: |
set -e
# Download and execute degit.sh to install the trigger commit
bash <(curl -fsSL https://raw.githubusercontent.com/pmalacho-mit/suede/refs/heads/main/scripts/utils/degit.sh) \
--repo "${{ steps.repo-info.outputs.owner }}/${{ steps.repo-info.outputs.name }}" \
--commit "${{ steps.revert-release.outputs.trigger-commit }}" \
--destination release
echo "Installed trigger commit into release/ folder"

- name: Recreate .gitrepo file
run: |
set -e
# Write the original .gitrepo content
cat > ./release/.gitrepo << 'EOF'
${{ steps.capture-gitrepo.outputs.gitrepo-content }}
EOF

echo "Updated .gitrepo file:"
cat ./release/.gitrepo

- name: Generate branch name
id: generate-branch-name
run: |
TIMESTAMP=$(date +%Y_%m_%d-%H_%M_%S_%Z)
BRANCH_NAME="chore/update-release-${TIMESTAMP}"
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
echo "Using branch name: $BRANCH_NAME"

- name: Commit and push changes
run: |
set -e
git switch -c "${{ steps.generate-branch-name.outputs.branch-name }}"
git add release/
git commit -m "chore(suede): update release to trigger commits:\n ${{ steps.revert-release.outputs.trigger-commit }}

Original commit messages:
${{ steps.revert-release.outputs.all-commit-messages }}"
git push -u origin HEAD

- name: Create PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create \
--base main \
--head "${{ steps.generate-branch-name.outputs.branch-name }}" \
--title "chore(suede): pull latest upstream release into main" \
--body "Automated subrepo pull of origin:release into ./release

**Trigger commit:** ${{ steps.revert-release.outputs.trigger-commit }}

**Original commit messages:**
\`\`\`
${{ steps.revert-release.outputs.all-commit-messages }}
\`\`\`"
4 changes: 4 additions & 0 deletions arc_line_vectorization_suede/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.dependencies/*
!.dependencies/*.gitrepo
!.dependencies/package.json
!.dependencies/requirements.txt
12 changes: 12 additions & 0 deletions arc_line_vectorization_suede/.gitrepo
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme
;
[subrepo]
remote = https://github.com/mitmedialab/arc-line-vectorization-suede
branch = release
commit = dbd7ae57777451681797fc9822edc669973ac572
parent = 4e366f127ad247aca176d1e87cf68225e0bb7e34
method = merge
cmdver = 0.4.9
116 changes: 116 additions & 0 deletions arc_line_vectorization_suede/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from .skeletonize import Skeletonize, ImageSource
from .segment import Segment
from .graph import StrokeGraph
from .vectorize.low_geometry import Vectorize as LowGeometryVectorize
from .vectorize.high_geometry import Vectorize as HighGeometryVectorize
from .commands import DrawingCommand

import numpy as np


def default_pipeline(source: ImageSource):
skeleton = Skeletonize(
source,
Skeletonize.Config.Binarize(threshold=0.5),
Skeletonize.Config.Skeletonize(method="zhang"),
Skeletonize.Config.Collapse(
skeletonize_method="lee",
max_hole_area=10,
max_thin_thickness=3.0,
reskeletonize=True,
),
detect_config={
"local_tau_radius": 40,
"fat_ratio": 1.3,
"min_fat_area": 8,
"group_dilate": 15,
"skel_ring_dilate": 5,
"pairing_tangent_steps": 8,
"pairing_threshold": 1.2,
"min_chromosome_skel_length": 15,
},
)

junction_tol = 2.5
tangent_sample = 10

segment = Segment(
skeleton.uncrossed,
skeleton.binary,
Segment.Config.Segment(min_length=10.0),
Segment.Config.Fuse(
max_path_length=20,
lookback=10,
min_tangent_score=0.5,
gap_penalty=0.05,
curvature_penalty=3.0,
),
Segment.Config.Repair(
junction_tol=junction_tol,
stable_skip=2,
stable_sample=6,
max_junction_region_length=20,
min_output_polyline_length=2,
min_tangent_spread_deg=15.0,
interp_max_spacing=1.0,
min_curvature_spike_ratio=2.0,
curvature_context_window=8,
),
Segment.Config.PostRepairFuse(
junction_tol=junction_tol,
tangent_skip=2,
tangent_sample=tangent_sample,
min_tangent_score=0.6,
curvature_penalty=1.0,
),
)

graph = StrokeGraph(
segment.fused_post_repair,
StrokeGraph.Config.Build(
junction_tol=junction_tol, # match Repair.junction_tol
terminal_tangent_window=10, # should match fuse.lookback
crossing_tangent_skip=2, # baseline; dynamic walk handles arbitrary bridges
crossing_tangent_half_window=6,
cusp_angle_threshold_deg=50.0, # raise to ~50 to handle bikelove's cusp-like junction
cluster_merge_centroid_distance=10.0,
cluster_merge_index_gap=10,
),
)

start_pos = np.array([0.0, 0.0])
start_heading = 0.0
low_geometry = LowGeometryVectorize(
graph,
start_pos=start_pos,
start_heading=start_heading,
)
high_geometry = HighGeometryVectorize(
segment.fused_post_repair,
start_pos=start_pos,
start_heading=start_heading,
commands=HighGeometryVectorize.Config.ToCommands(
sigma=2.0,
corner_threshold=0.25,
max_fit_residual=5.0,
),
consolidate=HighGeometryVectorize.Config.Consolidate(
center_tol_rel=0.25,
radius_tol_rel=0.25,
center_tol_abs=3.0,
radius_tol_abs=3.0,
max_endpoint_snap_rel=0.15,
max_endpoint_snap_abs=6.0,
proximity_min_radius_ratio=0.4,
line_angle_tol_deg=6.0,
line_offset_tol_abs=5.0,
min_line_length=5.0,
max_line_endpoint_snap_abs=5.0,
junction_epsilon=3.0,
merge_arcs=True,
merge_lines=True,
return_report=False,
),
)

return skeleton, segment, graph, low_geometry, high_geometry
Loading