Crafty Computation: Cross-Stitch Patterns with the Wolfram Language

December 23, 2019

Jon McLoone, Director, Technical Communication & Strategy

For many of us, programming represents leisure time just as much as work. Here at Wolfram, we have an incredibly creative group with a wide variety of hobbies, on the screen and off—including textile arts like cross-stitch. So when my colleague Jay suggested that I create a cross-stitch program using the Wolfram Language, I replied with “Challenge accepted!” Jay was looking for a simple way to generate a cross-stitch pattern from a photograph—or really any image—with the colors corresponding to the DMC thread ID numbers. We both knew that the image-processing capabilities of the Wolfram Language would make this an easy task, but incorporating the DMC thread catalog seemed a more interesting challenge. Armed with both computer and (virtual) thread, I set out on my quest to create the perfect cross-stitch pattern generator.

Converting the Image It's actually quite easy to translate a shape into a cross-stitch pattern with the Wolfram Language. Each stitch becomes a “pixel,” and each color a thread. The first things we want to do are reduce the resolution of the image so that we do not have thousands of stitches to do, and reduce the number of colors so that we don’t have to manage too many threads:

simplifyImage[img_Image,size_,colors_]:=ColorQuantize[ImageResize[RemoveAlphaChannel[img,White],size],colors,Dithering->False]

Then we choose our image. For our example, I decided on our favorite rhombic hexecontahedron, the Mathematica 8 variant of the Spikey! We will use a 50×50 grid and 12 different colored threads:

spikey=simplifyImage[,50,12]

By de-duplicating the RGB values of the image, we get the list of colors that we need to match to:

colorList[img_Image]:=RGBColor@@@Union[Flatten[ImageData[img],1]]

This list should be 12 colors long, since that is what we asked for:

colorList[spikey]

Threading the Needle  We now need to map these colors to the nearest color threads that we can purchase. One popular brand is DMC. I found two lists of their palette in RGB specifications that differed by small amounts and a website with photographs of every thread. I couldn’t decide which was the best source, so I used Blend to find the perceptual average of all three sources to make my own version of the DMC thread catalog. The images were first converted to colors using DominantColors:

DMCColorDS=

A small restructuring of this data makes it more useful for color matching:

Short[colorLookupData=Normal[DMCColorDS[All,#Color->#&]]]

Nearest can now automatically apply a perceptual measure of color distance to helps us match colors:

nearestColor[col_]:=First[Nearest[colorLookupData,col]]

For example, if we want Pink, then our best thread to purchase is called “Melon Medium,” with a catalog ID of 3706:

nearestColor[Pink]

We can now use this function on each of the colors in the reduced color palette, and at the same time associate a symbol with each color. The list of symbols is entirely arbitrary, but must be at least as long as the number of colors that we will use:

colorMatches[src_]:=MapIndexed[(#1->Append[nearestColor[#1],"Key"->Extract[{"■","●","▲","◆","▼","★","♣","♡","\[MathematicaIcon]","\[WolframLanguageLogoCircle]","○",".","×","+","⌘","γ","✓","π","♯","⌚","♖","♮","~","ψ"," "},#2]])&,colorList[src]];
Short[replacements=colorMatches[spikey]]

A little styling information, and we can display this generated key:

keyTable[data_]:=TextGrid[Prepend[{Style[#Key,28],Framed["    ",Background->#Color],#Name,#ID}&/@Last/@data,Style[#,Darker@Red,28]&/@{"Key","Color","Name","Floss ID"}],Frame->All,BaseStyle->20];
keyTable[replacements]

And we need to apply that key to the image data to make a grid of the matching key symbols:

mesh10={Thickness[2],True,True,True,True,True,True,True,True,True};

sewingPattern[simplifiedImage_,replacementRules_]:=Grid[Map[((RGBColor@@#)/.replacementRules)["Key"]&,ImageData[simplifiedImage],{2}],Spacings->0,ItemSize->{1,1},
Dividers -> {{mesh10},{mesh10}}];
sewingPattern[spikey,replacements]

And that’s our Spikey pattern, ready to stitch!

The final steps are to put all the code together, add a user interface and deploy it to the Wolfram Cloud, so that everyone in the world can benefit:

CloudDeploy[
(*User Interface*)
FormPage[
{"Image"->"Image","Size"->{30,50,70,100},"Colors"->{10,15,20,25}},

(*Pre-processing*)
Block[{simplified,replacements},
simplified=simplifyImage[#Image,#Size,#Colors];
replacements=colorMatches[simplified];

(*Output*)
Column[Rasterize/@{
ImageResize[simplified,400,Resampling->"Nearest"],
keyTable[replacements],
sewingPattern[simplified,replacements]}]
]&,

(*Text contents*)
AppearanceRules-><|"Title"->"Cross-stitch Pattern Generator","Description"->"Upload an image to convert it to a simplified cross-stitch pattern in DMC thread colors. <BR>Choose larger sizes and more colors for a more life-like result."
|>
],
(*Deployment information*)
"sewingpatterns",Permissions->"Public"]

And as a small variation on the theme, here is an application for cross-stitching words in a variety of fonts:

CloudDeploy[
(*User Interface*)
FormPage[
{"Text"->"String","Size"->{50,100,150},"Colors"->{2,3,4}, "Font"->CloudEvaluate[$FontFamilies]->"Times"},

(*Pre-processing*)
Block[{simplified,replacements},
simplified=simplifyImage[Rasterize[Style[#Text,FontFamily->#Font,FontSize->#Size]],#Size,#Colors];
replacements=MapIndexed[(#1->Append[nearestColor[#1],"Key"->Extract[{"■","○","."," "},#2]])&,colorList[simplified]];

(*Output*)
Column[Rasterize/@{
ImageResize[simplified,400,Resampling->"Nearest"],
keyTable[replacements],
sewingPattern[simplified,replacements]}]
]&,

(*Text contents*)
AppearanceRules-><|"Title"->"Cross-stitch Pattern Generator","Description"->"Convert text to a simplified cross-stitch pattern in DMC thread colors. <BR>Choose larger sizes and more colors for a more accurate result."
|>
],
(*Deployment information*)
"textsewingpatterns",Permissions->"Public"]

The Proof Is in the Pattern  This pattern generator worked well in a Wolfram Notebook, but the real test was to bring this pattern to life with needle and thread. If Jay’s handiwork is any indication, I think we can say that the cross-stitch generator is a success:

And best of all, you can test it out for yourself—both my cross-stitch pattern generator and text pattern generator are available on the Wolfram Cloud for anyone to use! Simply upload your own image for a custom cross-stitch pattern. You can also build off of the code in this post to create a database of your favorite thread brand. And with the holiday season here, the generator is a great help in creating your unique gifts for lucky loved ones, or can be your break from the computer screen as you cozy up by the fire. Happy holidays, and happy stitching!

For Downloadable Notebook: Creating the Thread Catalog

newdata

newnames=newdata[[All,2]];

oldnames=Last[#]["Name"]&/@colorData;

store=EntityStore["DMCColors"-><|
"Entities"-><|
"3811"-><|"Name"->"Turquoise - VY LT","Color"->Red|>
|>
|>]

EntityRegister[store]

Entity["DMCColors","3811"]["Name"]

Entity["DMCColors","3811"]["Color"]

Next

Import["https://www.csh.rit.edu/~vance/pages/color.html","Data"]

Length[%]