Convert this code into Python
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

I have a Mathematica function called ImageColors defined below that I don't have time to rewrite in Python (version 3+). It takes any image and produces a list of the dominant colors in the image along with masks where that color is found, which is basically a custom version of the function DominantColors.

  • Here are some examples of it working: https://imgur.com/a/DVfzNen
  • Please use efficient mainstream python packages like cv2
  • It should work efficiently on larger sized images too (~10 megapixels)
  • Preferably you would reimplement the same basic subroutines, keeping as much of the functional structure as possible

Here's the source code:

ff[x_] := If[((x) > 0.00885645), (x^(1/3)), (7.787*(x) + 0.137931)];
RGBToLAB[{r_, g_, b_, _}] := RGBToLAB[{r, g, b}];
RGBToLAB[{r_, g_, b_}] := Module[
    {x, y, z, fy}, 
    {x, y, z} = {{0.412387, 0.357592, 0.180451}, {0.212637, 0.715183, 0.0721803}, {0.0193306, 0.119197, 0.950372}}.{r, g, b};
    x = x/.950429; z = z/1.0889; fy = ff[y];
    .01*{116*fy - 16, 500*(ff[x] - fy), 200*(fy - ff[z])}
];

ColorsEqual[a_, b_, d_] := EuclideanDistance[RGBToLAB @ a, RGBToLAB @ b] < d;

WeightedMean[weights_, list_] := (weights . list) / Total[weights];
WeightedMean[{},_] := {0,0};
WeightedMean[list_] := Mean[list];

Options[PickKExamples] = {"DistanceFunction" -> EuclideanDistance};
PickKExamples[weights_ -> list_, k_, opts:OptionsPattern[]] := PickKExamples[list, k, opts]; 
PickKExamples[list_, k_, OptionsPattern[]] := Module[
    {examples, unused, nearest, dist = OptionValue["DistanceFunction"]},
    examples = {First[list]};
    unused = Rest[list];  
    While[Length[examples] < k && unused =!= {},
        nearest = Nearest[examples, DistanceFunction -> dist];
        unused = SortBy[unused, dist[#, First[nearest[#,1]]]&]; (* obviously way inefficient, but who cares *)
        AppendTo[examples, Last[unused]];
        unused = Most[unused];  
    ];
    examples
];

BinarizeColor[img_Image, color_List, stddev_] := With[
    {c = MapThread[Binarize[#1, {#2 - stddev, #2 + stddev}]&, 
        {ColorSeparate[img], color}]},
    ImageMultiply[c[[1]], ImageMultiply[c[[2]], c[[3]]]]];

EstimateColorArea[img_Image, h:(_RGBColor|_Hue|_GrayLevel), sd_] := EstimateColorArea[img, List @@ ColorConvert[h, RGBColor], sd];
EstimateColorArea[img_Image, rgb_List, sd_] := ImageLevels[BinarizeColor[img, rgb, sd]][[2,2]];

ColorRegions[img_] := With[
    {a = Times @@ ImageDimensions[img]},
    DeleteSmallComponents[
        MorphologicalComponents[
            ColorNegate @ Thinning[Dilation[EdgeDetect[img], Ceiling[Sqrt[a] / 200]], Padding -> 1],
            CornerNeighbors -> False
        ],
    a/2000]
];
Clear@ImageColors
ImageColors[img_Image] := Module[{comps, colors, deviation, clusters = None, area, iters = 0, colors1, thumb},
    area = N[Times @@ ImageDimensions[img]];
    comps = ColorRegions[img];
    colors1 = colors = {#1, RootMeanSquare[#3], #2}& @@@ Last /@ ComponentMeasurements[{comps,img}, {"Median", "Count", "StandardDeviation"}, #2>20&];
    If[Length[colors] < 2, 
        deviation = 0.1,
        deviation = Max[Quantile[colors[[All,2]], 0.9], 0.05];
    ];
    colors = Select[colors, #[[2]] < deviation &];
    deviation = Max[deviation,0.04];
    (*Print[MapColumn[1, RGBColor, colors]];*)
    While[(clusters === None || Length[clusters] > 8) && iters++ < 10,
        clusters = {
                WeightedMean[#[[All,3]]^3,#[[All,1]]], (* color *)
                Total[#[[All,3]]]/area, (* area *)
                Max[Sqrt @ WeightedMean[#[[All,3]], #[[All,2]]^2], 0.005] (* std-dev *)
            }& /@ Gather[colors, ColorsEqual[First[#1], First[#2], deviation]&];
        deviation *= 1.5;
    ];
    thumb = Thumbnail[img, 60, Resampling -> "Nearest"];
    clusters = {RGBColor[#1], EstimateColorArea[img, #1, 0.1]/area, ColorNegate @ BinarizeColor[thumb, #1, 2 * #3]}& @@@ clusters;
    clusters = SortBy[clusters, -#[[2]]&];
    PickKExamples[clusters, Infinity, "DistanceFunction" -> Function[EuclideanDistance[#1[[1, 1]], #2[[1, 1]]]]] (* has the effect of ordering by distance *)
];
This may not be a complete answer, but it may help: https://github.com/gwiederhecker/ToPython
sharper 5 months ago
@sharper Those type of conversions are built-in to Mathematica: https://reference.wolfram.com/language/ref/format/PythonExpression.html
mr142 5 months ago

Crowdsource coding tasks.

1 Solution


import the necessary packages

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import argparse
import utils
import cv2

construct the argument parser and parse the arguments

ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
ap.add_argument("-c", "--clusters", required = True, type = int,
help = "# of clusters")
args = vars(ap.parse_args())

load the image and convert it from BGR to RGB so that

we can dispaly it with matplotlib

image = cv2.imread(args["image"])
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

show our image

plt.figure()
plt.axis("off")
plt.imshow(image)

reshape the image to be a list of pixels

image = image.reshape((image.shape[0] * image.shape[1], 3))

cluster the pixel intensities

clt = KMeans(n_clusters = args["clusters"])
clt.fit(image)

import the necessary packages

import numpy as np
import cv2

def centroid_histogram(clt):
# grab the number of different clusters and create a histogram
# based on the number of pixels assigned to each cluster
numLabels = np.arange(0, len(np.unique(clt.labels_)) + 1)
(hist, ) = np.histogram(clt.labels, bins = numLabels)

# normalize the histogram, such that it sums to one
hist = hist.astype("float")
hist /= hist.sum()

# return the histogram
return hist

def plot_colors(hist, centroids):
# initialize the bar chart representing the relative frequency
# of each of the colors
bar = np.zeros((50, 300, 3), dtype = "uint8")
startX = 0

# loop over the percentage of each cluster and the color of
# each cluster
for (percent, color) in zip(hist, centroids):
    # plot the relative percentage of each cluster
    endX = startX + (percent * 300)
    cv2.rectangle(bar, (int(startX), 0), (int(endX), 50),
        color.astype("uint8").tolist(), -1)
    startX = endX

# return the bar chart
return bar

build a histogram of clusters and then create a figure

representing the number of pixels labeled to each color

hist = utils.centroid_histogram(clt)
bar = utils.plot_colors(hist, clt.cluster_centers_)

show our color bart

plt.figure()
plt.axis("off")
plt.imshow(bar)
plt.show()

I asked for my algorithm not a new one.
mr142 5 months ago
Thanks, but this is nothing like what I asked for
mr142 5 months ago
My apologies. But is this for a university assignment? I have a few if you're interested.
Mealoshe 5 months ago
No it’s just for personal side projects
mr142 5 months ago