Skip to content

Commit f4821fc

Browse files
committed
basically working
1 parent 5909f65 commit f4821fc

File tree

5 files changed

+466
-11
lines changed

5 files changed

+466
-11
lines changed

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ User guide
2121

2222
readme
2323
userguide/Unmap_data_from_an_image.ipynb
24+
userguide/Guess_the_colourmap_from_an_image.ipynb
2425

2526

2627
API reference

docs/notebooks/Guess_the_colourmap_from_an_image.ipynb

Lines changed: 434 additions & 0 deletions
Large diffs are not rendered by default.

docs/notebooks/Unmap_data_from_an_image.ipynb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"- **You don't have a lot of pixels.** Small images are hard to rip data from. We'd always like more pixels.\n",
1717
"- **There are a lot of annotations.** Usually these get in the way of the data.\n",
1818
"- **The image is lossily compressed.** Most PDFs contain JPEGs and JPEG is [lossy](https://en.wikipedia.org/wiki/Lossy_compression). This means the colours are a bit garbled, especially around abrupt edges (like annotations!).\n",
19-
"- **The image has hillshading.** The cute shadow effect adds another layer of complexity... but we can still have a go. \n",
19+
"- **The image has dithering.** If the number of colours in the image has been reduced at some point, there's a good chance it has been [dithered](https://en.wikipedia.org/wiki/Dither).\n",
20+
"- **The image has hillshading or specularity.** Cute 3D effects add another layer of complexity... but we can still have a go. \n",
2021
"- **It's a 3D perspective plot.** You might be able to recover the data from what you can _see_, but 3D perspective views add another type of distortion that I daresay could be undone, but not by me. \n",
2122
"- **The image is poor.** If it's a photo or scan of a paper document... well, now you 've got the unknown transform from the data to the image, then the unknown transform of the image to the physical plot, then the unknown transform of the plot to the image you have. I mean, come on.\n",
2223
"\n",

unmap/unmap.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from scipy.spatial import cKDTree
1515
from scipy.cluster.vq import kmeans
1616
from matplotlib import cm
17-
from matplotlib.colors import hsv_to_rgb, rgb_to_hsv
17+
from matplotlib.colors import ListedColormap, hsv_to_rgb, rgb_to_hsv
1818
from matplotlib.colors import to_rgb, LinearSegmentedColormap
1919

2020

@@ -147,6 +147,9 @@ def get_cmap(cmap, arr=None, levels=256, quantize=False):
147147
except ValueError:
148148
raise ValueError("cmap name not recognized by matplotlib")
149149

150+
elif isinstance(cmap, LinearSegmentedColormap) or isinstance(cmap, ListedColormap):
151+
return cmap
152+
150153
else:
151154
try:
152155
cmap = np.array(cmap)

unmap/unweave.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def construct_graph(imarray, colors=256, normed=True):
6565

6666
# Normalize.
6767
if normed:
68-
glcm /= (1e-9 + np.sum(glcm, axis=-1))
68+
glcm = glcm / (1e-9 + np.sum(glcm, axis=-1))
6969

7070
# Construct and remove self-loops.
7171
G = nx.from_numpy_array(glcm)
@@ -164,7 +164,7 @@ def longest_shortest_path(G):
164164
path (list): Longest shortest path.
165165
"""
166166

167-
dist = lambda *_, d: d['dist']**2
167+
dist = lambda u, v, d: d['dist']**2
168168

169169
# Find the longest shortest path.
170170
paths = nx.shortest_path_length(G, weight=dist)
@@ -181,7 +181,7 @@ def longest_shortest_path(G):
181181
target=t)
182182

183183

184-
def path_to_cmap(path, unique_colors, colors=256, reverse='auto'):
184+
def path_to_cmap(path, unique_colors, colors=256, reverse='auto', equilibrate=False):
185185
"""
186186
Convert a path through the graph to a colormap.
187187
@@ -193,11 +193,14 @@ def path_to_cmap(path, unique_colors, colors=256, reverse='auto'):
193193
reverse (bool): Whether to reverse the colormap. If 'auto', the
194194
colormap will start with the end closest to dark blue. If False,
195195
the direction is essentially random.
196+
equilibrate (bool): Whether to equilibrate the colormap. This will
197+
try to ensure that the colormap's colors are as evenly spaced as
198+
possible.
196199
197200
Returns:
198201
matplotlib.colors.LinearSegmentedColormap: Colormap.
199202
"""
200-
cpath = [unique_colors[n] for n in path]
203+
cpath = np.asarray(unique_colors)[path]
201204
if reverse == 'auto':
202205
cool_dark = np.array([0, 0, 0.5])
203206
if np.linalg.norm(cpath[0] - cool_dark) > np.linalg.norm(cpath[-1] - cool_dark):
@@ -208,10 +211,23 @@ def path_to_cmap(path, unique_colors, colors=256, reverse='auto'):
208211
cpath = cpath[::-1]
209212
if colors is None:
210213
colors = 2 * len(cpath) # Not sure what the default should be.
211-
return LinearSegmentedColormap.from_list("recovered", cpath, N=colors)
212-
213-
214-
def guess_cmap(fname, source_colors=256, target_colors=256, min_weight=0.025, max_dist=0.25, max_neighbours=20, reverse='auto', ):
214+
cmap = LinearSegmentedColormap.from_list("recovered", cpath, N=colors)
215+
if equilibrate:
216+
dists = np.linalg.norm(cpath[:-1] - cpath[1:], axis=-1)
217+
invdist = np.cumsum(1 / dists) / (1 / dists).sum()
218+
cmap = LinearSegmentedColormap.from_list('recovered', cmap(invdist), N=colors)
219+
return cmap
220+
221+
222+
def guess_cmap(fname,
223+
source_colors=256,
224+
target_colors=256,
225+
min_weight=0.025,
226+
max_dist=0.25,
227+
max_neighbours=20,
228+
reverse='auto',
229+
equilibrate=False
230+
):
215231
"""
216232
Guess the colormap of an image.
217233
@@ -231,4 +247,4 @@ def guess_cmap(fname, source_colors=256, target_colors=256, min_weight=0.025, ma
231247
G = construct_graph(imarray, colors=source_colors)
232248
G0 = prune_graph(G, uniq, min_weight=min_weight, max_dist=max_dist, max_neighbours=max_neighbours)
233249
path = longest_shortest_path(G0)
234-
return path_to_cmap(path, uniq, colors=target_colors, reverse=reverse)
250+
return path_to_cmap(path, uniq, colors=target_colors, reverse=reverse, equilibrate=equilibrate)

0 commit comments

Comments
 (0)