Repository: Image-Py/sknw
Branch: master
Commit: 68cf72383496
Files: 6
Total size: 11.8 KB
Directory structure:
gitextract_ypbsjqkh/
├── .gitignore
├── LICENSE
├── README.md
├── setup.py
└── sknw/
├── __init__.py
└── sknw.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2017, Yan xiaolong
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
Skeleton Network
======================
build net work from nd skeleton image
### graph = sknw.build_sknw(ske, multi=False, iso=True, ring=True, full=True)
> **ske:** should be a nd skeleton image
>
> **multi:** if True,a multigraph is retured, which allows more than one edge between two nodes and self-self edge.
>
> **iso:** if True, return one-pixel node
>
> **ring:** if True, return ring without any branch (and insert a self-connected node in the ring)
>
> **full:** if True, every edge start from the node's centroid. else touch the node block but not from the centroid.
>
> **return:** is a networkx Graph object

### graph detail:
> **graph.nodes[id]['pts'] :** Numpy(x, n), coordinates of nodes points
>
> **graph.nodes[id]['o']:** Numpy(n), centroid of the node
>
> **graph.edge(id1, id2)['pts']:** Numpy(x, n), sequence of the edge point
>
> **graph.edge(id1, id2)['weight']:** float, length of this edge
>
> *if it's a multigraph, you must add a index after two node id to get the edge, like: graph.edge(id1, id2)[0].*
### Build Graph:
build Graph by Skeleton, then plot as a vector Graph in matplotlib.
```python
from skimage.morphology import skeletonize
from skimage import data
import sknw
# open and skeletonize
img = data.horse()
ske = skeletonize(~img).astype(np.uint16)
# build graph from skeleton
graph = sknw.build_sknw(ske)
# draw image
plt.imshow(img, cmap='gray')
# draw edges by pts
for (s,e) in graph.edges():
ps = graph[s][e]['pts']
plt.plot(ps[:,1], ps[:,0], 'green')
# draw node by o
nodes = graph.nodes()
ps = np.array([nodes[i]['o'] for i in nodes])
plt.plot(ps[:,1], ps[:,0], 'r.')
# title and show
plt.title('Build Graph')
plt.show()
```

### Find Path
then you can use networkx do what you want

### 3D Skeleton
sknw can works on nd image, this is a 3d demo by mayavi

### About ImagePy
[https://github.com/Image-Py/imagepy](https://github.com/Image-Py/imagepy)
ImagePy is my opensource image processihng framework. It is the ImageJ of Python, you can wrap any numpy based function esaily. And sknw is a sub module of ImagePy. You can use sknw without any code.

================================================
FILE: setup.py
================================================
from setuptools import setup
descr = """sknw: skeleton analysis in Python.
Inspired by Juan Nunez-Iglesias's skan.
"""
if __name__ == '__main__':
setup(name='sknw',
version='0.13',
url='https://github.com/yxdragon/sknw',
description='Analysis of object skeletons',
long_description=descr,
author='YXDragon',
author_email='yxdragon@imagepy.org',
license='BSD 3-clause',
packages=['sknw'],
package_data={},
install_requires=[
'numpy',
'networkx',
'numba'
],
)
================================================
FILE: sknw/__init__.py
================================================
from .sknw import mark, parse_struc, build_graph, draw_graph, build_sknw
__version__ = '0.1'
__all__ = ['mark', 'parse_struc', 'build_graph', 'draw_graph', 'build_sknw']
def test():
from skimage.morphology import skeletonize
import numpy as np
from skimage import data
import matplotlib.pyplot as plt
img = data.horse()
ske = skeletonize(~img).astype(np.uint16)
graph = build_sknw(ske)
plt.imshow(img, cmap='gray')
for (s,e) in graph.edges():
ps = graph[s][e]['pts']
plt.plot(ps[:,1], ps[:,0], 'green')
nodes = graph.nodes()
ps = np.array([nodes[i]['o'] for i in nodes])
plt.plot(ps[:,1], ps[:,0], 'r.')
plt.title('Build Graph')
plt.show()
================================================
FILE: sknw/sknw.py
================================================
import numpy as np
from numba import jit
import networkx as nx
def neighbors(shape):
dim = len(shape)
block = np.ones([3]*dim)
block[tuple([1]*dim)] = 0
idx = np.where(block>0)
idx = np.array(idx, dtype=np.uint8).T
idx = np.array(idx-[1]*dim)
acc = np.cumprod((1,)+shape[::-1][:-1])
return np.dot(idx, acc[::-1])
@jit(nopython=True) # my mark
def mark(img, nbs): # mark the array use (0, 1, 2)
img = img.ravel()
for p in range(len(img)):
if img[p]==0:continue
s = 0
for dp in nbs:
if img[p+dp]!=0:s+=1
if s==2:img[p]=1
else:img[p]=2
@jit(nopython=True) # trans index to r, c...
def idx2rc(idx, acc):
rst = np.zeros((len(idx), len(acc)), dtype=np.int16)
for i in range(len(idx)):
for j in range(len(acc)):
rst[i,j] = idx[i]//acc[j]
idx[i] -= rst[i,j]*acc[j]
rst -= 1
return rst
@jit(nopython=True) # fill a node (may be two or more points)
def fill(img, p, num, nbs, acc, buf):
img[p] = num
buf[0] = p
cur = 0; s = 1; iso = True;
while True:
p = buf[cur]
for dp in nbs:
cp = p+dp
if img[cp]==2:
img[cp] = num
buf[s] = cp
s+=1
if img[cp]==1: iso=False
cur += 1
if cur==s:break
return iso, idx2rc(buf[:s], acc)
@jit(nopython=True) # trace the edge and use a buffer, then buf.copy, if use [] numba not works
def trace(img, p, nbs, acc, buf):
c1 = 0; c2 = 0;
newp = 0
cur = 1
while True:
buf[cur] = p
img[p] = 0
cur += 1
for dp in nbs:
cp = p + dp
if img[cp] >= 10:
if c1==0:
c1 = img[cp]
buf[0] = cp
else:
c2 = img[cp]
buf[cur] = cp
if img[cp] == 1:
newp = cp
p = newp
if c2!=0:break
return (c1-10, c2-10, idx2rc(buf[:cur+1], acc))
@jit(nopython=True) # parse the image then get the nodes and edges
def parse_struc(img, nbs, acc, iso, ring):
img = img.ravel()
buf = np.zeros(131072, dtype=np.int64)
num = 10
nodes = []
for p in range(len(img)):
if img[p] == 2:
isiso, nds = fill(img, p, num, nbs, acc, buf)
if isiso and not iso: continue
num += 1
nodes.append(nds)
edges = []
for p in range(len(img)):
if img[p] <10: continue
for dp in nbs:
if img[p+dp]==1:
edge = trace(img, p+dp, nbs, acc, buf)
edges.append(edge)
if not ring: return nodes, edges
for p in range(len(img)):
if img[p]!=1: continue
img[p] = num; num += 1
nodes.append(idx2rc([p], acc))
for dp in nbs:
if img[p+dp]==1:
edge = trace(img, p+dp, nbs, acc, buf)
edges.append(edge)
return nodes, edges
# use nodes and edges build a networkx graph
def build_graph(nodes, edges, multi=False, full=True):
os = np.array([i.mean(axis=0) for i in nodes])
if full: os = os.round().astype(np.uint16)
graph = nx.MultiGraph() if multi else nx.Graph()
for i in range(len(nodes)):
graph.add_node(i, pts=nodes[i], o=os[i])
for s,e,pts in edges:
if full: pts[[0,-1]] = os[[s,e]]
l = np.linalg.norm(pts[1:]-pts[:-1], axis=1).sum()
graph.add_edge(s,e, pts=pts, weight=l)
return graph
def mark_node(ske):
buf = np.pad(ske, (1,1), mode='constant').astype(np.uint16)
nbs = neighbors(buf.shape)
acc = np.cumprod((1,)+buf.shape[::-1][:-1])[::-1]
mark(buf, nbs)
return buf
def build_sknw(ske, multi=False, iso=True, ring=True, full=True):
buf = np.pad(ske, (1,1), mode='constant').astype(np.uint16)
nbs = neighbors(buf.shape)
acc = np.cumprod((1,)+buf.shape[::-1][:-1])[::-1]
mark(buf, nbs)
nodes, edges = parse_struc(buf, nbs, acc, iso, ring)
return build_graph(nodes, edges, multi, full)
# draw the graph
def draw_graph(img, graph, cn=255, ce=128):
acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1]
img = img.ravel()
for (s, e) in graph.edges():
eds = graph[s][e]
if isinstance(graph, nx.MultiGraph):
for i in eds:
pts = eds[i]['pts']
img[np.dot(pts, acc)] = ce
else: img[np.dot(eds['pts'], acc)] = ce
for idx in graph.nodes():
pts = graph.nodes[idx]['pts']
img[np.dot(pts, acc)] = cn
if __name__ == '__main__':
import matplotlib.pyplot as plt
img = np.array([
[0,0,0,1,0,0,0,0,0],
[0,0,0,1,0,0,0,1,0],
[0,0,0,1,0,0,0,0,0],
[1,1,1,1,0,0,0,0,0],
[0,0,0,0,1,0,0,0,0],
[0,1,0,0,0,1,0,0,0],
[1,0,1,0,0,1,1,1,1],
[0,1,0,0,1,0,0,0,0],
[0,0,0,1,0,0,0,0,0]])
node_img = mark_node(img)
para = [{'iso':False}, {'ring':False}, {'full':False},
{'iso':True}, {'ring':True}, {'full':True}]
for i,p,k in zip([1,2,3,4,5,6], [231,232,233,234,235,236], para):
print(k)
graph = build_sknw(img, False, **k)
ax = plt.subplot(p)
ax.imshow(node_img[1:-1,1:-1], cmap='gray')
# draw edges by pts
for (s,e) in graph.edges():
ps = graph[s][e]['pts']
ax.plot(ps[:,1], ps[:,0], 'green')
# draw node by o
nodes = graph.nodes()
ps = np.array([nodes[i]['o'] for i in nodes])
ax.plot(ps[:,1], ps[:,0], 'r.')
# title and show
ax.title.set_text(k)
plt.show()
gitextract_ypbsjqkh/
├── .gitignore
├── LICENSE
├── README.md
├── setup.py
└── sknw/
├── __init__.py
└── sknw.py
SYMBOL INDEX (11 symbols across 2 files) FILE: sknw/__init__.py function test (line 8) | def test(): FILE: sknw/sknw.py function neighbors (line 5) | def neighbors(shape): function mark (line 16) | def mark(img, nbs): # mark the array use (0, 1, 2) function idx2rc (line 27) | def idx2rc(idx, acc): function fill (line 37) | def fill(img, p, num, nbs, acc, buf): function trace (line 56) | def trace(img, p, nbs, acc, buf): function parse_struc (line 80) | def parse_struc(img, nbs, acc, iso, ring): function build_graph (line 110) | def build_graph(nodes, edges, multi=False, full=True): function mark_node (line 122) | def mark_node(ske): function build_sknw (line 129) | def build_sknw(ske, multi=False, iso=True, ring=True, full=True): function draw_graph (line 138) | def draw_graph(img, graph, cn=255, ce=128):
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
{
"path": ".gitignore",
"chars": 1157,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": "LICENSE",
"chars": 1512,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2017, Yan xiaolong\nAll rights reserved.\n\nRedistribution and use in source and binary"
},
{
"path": "README.md",
"chars": 2449,
"preview": "Skeleton Network\n======================\nbuild net work from nd skeleton image\n\n### graph = sknw.build_sknw(ske, multi=Fa"
},
{
"path": "setup.py",
"chars": 592,
"preview": "from setuptools import setup\n\ndescr = \"\"\"sknw: skeleton analysis in Python.\nInspired by Juan Nunez-Iglesias's skan.\n\"\"\"\n"
},
{
"path": "sknw/__init__.py",
"chars": 718,
"preview": "from .sknw import mark, parse_struc, build_graph, draw_graph, build_sknw\n\n__version__ = '0.1'\n\n__all__ = ['mark', 'parse"
},
{
"path": "sknw/sknw.py",
"chars": 5680,
"preview": "import numpy as np\nfrom numba import jit\nimport networkx as nx\n\ndef neighbors(shape):\n dim = len(shape)\n block = n"
}
]
About this extraction
This page contains the full source code of the Image-Py/sknw GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (11.8 KB), approximately 3.7k tokens, and a symbol index with 11 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.