original = Import["https://hsto.org/files/f42/962/1c5/f429621c55cd46a1beb4d4eb5aefed53.jpg"]; sizeX = First@ImageDimensions[original]; clusters = ClusteringComponents[ original, 3, Method -> "KMeans", DistanceFunction -> CosineDistance ];
minIndex
, returns the position of the minimum element in the list, that is, the compiled substitution Composition[First, Ordering]
: minIndex = Compile[ { {list, _Real, 1} }, Module[ { i = 0, min = list[[1]], minPos = 1 }, Do[ If[list[[i]] < min, minPos = i; min = list[[i]]], {i, 1, Length@list} ]; minPos ], CompilationTarget -> "C" ];
removeDarkest
removes the darkest color - this is the background, we do not need it: removeDarkest[list_] := Delete[list, minIndex[Norm /@ list]] getBasisColors[image_, clusters_] := Module[ { data = ImageData[image], components = Union[Flatten @ clusters] }, Table[ Median[Join @@ Pick[data, clusters, component]], {component, components} ] ]
B1 = removeDarkest@getBasisColors[ColorNegate@original, clusters]
alpha = 0.5; basis = {B1[[1]], B1[[2]] - alpha Min[B1[[2]]/B1[[1]]] B1[[1]]}; metric = Outer[Dot, basis, basis, 1];
alpha
<1 is the only fitting parameter of the algorithm. For example, in order to get a picture at the beginning of the post, I had to put alpha
= 0.95.metric
matrix is the metric of the linear envelope of the basis vectors, it is the Gram matrix, which we need in the next section. reduceMetric = Compile[ { {metric, _Real, 2}, {index, _Integer} }, Transpose@Delete[Transpose@Delete[metric, index], index], CompilationTarget -> "C" ]; getComponents = Compile[ { {vec, _Real, 1}, {basis, _Real, 2}, {metric, _Real, 2} }, Module[ { covariant, contravariant = Table[0., {Length[basis]}], flag = True, subspace }, covariant = basis.vec; flag = If[ Det[metric] != 0, contravariant = Inverse[metric].covariant; FreeQ[Sign[contravariant], -1], False ]; Chop@If[ flag, contravariant, subspace = Table[ Insert[ getComponents[vec, Delete[basis, i], reduceMetric[metric, i]], 0., i], {i, 1, Length@basis} ]; subspace[[minIndex[-(subspace.covariant)]]] ] ], { {_minIndex, _Integer}, {_reduceMetric, _Real, 2}, {_getComponents, _Real, 1} }, CompilationTarget -> "C", RuntimeAttributes -> {Listable}, Parallelization -> True ]
getComponents[{0.1, 0.9}.basis, basis, metric] {0.1, 0.9}
flag = False
generated in two cases: the metric is irreversible (simplex of a smaller dimension than we thought) or at least one of the contravariant coordinates is negative (did not fall inside the simplex). In these cases, stupidly recursively iterate over all faces, and choose the projection on which is maximum. The projection onto the subspace of the basis e (i) is a convolution of covariant and contravariant coordinates v i v i . Now for sure.getComponents
on several cores. Understand laziness, let it remain on the conscience of developers. So just grind all 2736000 pixels: pixels = Join @@ ImageData[ColorNegate@original]; components = Table[ getComponents[pixel, basis, metric], {pixel, pixels} ];
data1 = (({1, 0} #).basis) & /@ components; ColorNegate[Image@Partition[data1, sizeX]]
data2 = (({0, 1} #).basis) & /@ components; ColorNegate@Image@Partition[data2, sizeX]
getComponents
any, even degenerate / redundant basis, and you will be happy: Module[ { basis = {{1, 0}, {0, 1}, {1, 1}}, metric }, metric = Outer[Dot, basis, basis, 1]; getComponents[{0.1, 0., 0.9}.basis, basis, metric] ] {0.1, 0., 0.9}
Source: https://habr.com/ru/post/266101/
All Articles