8
$\begingroup$

In this video, she mentions that these patterns are 2-colorable (or at least conjectures that) I want to make a knitting pattern out of these, and while I have managed to figure out the lines defining the regions with careful dashing and line placements, I don't know how to color the map besides just making it and then into paint with the bucket tool. Is there any sort of map coloring function that I could use to avoid having to do it manually?

An example input and output to this hypothetical function are below. The thinner, more dashed lines should be ignored for the purposes of the map coloring.

Example Input to hypothetical coloring function Example Output of hypothetical coloring function

Edit: On request, the code used to generate this:

UnitSize = 15 ;
VerticalON[loc_, hmesslen_] := {
  AbsoluteDashing[2], Line[{
    Offset[{UnitSize*loc, 0}, {0, 0}],
    Offset[{UnitSize*loc, UnitSize*(hmesslen)}, {0, 0}]
    }],
  AbsoluteThickness[2], AbsoluteDashing[UnitSize], Line[{
    Offset[{UnitSize*loc, 0}, {0, 0}],
    Offset[{UnitSize*loc, UnitSize*(hmesslen)}, {0, 0}]
    }]
  }
VerticalOFF[loc_, hmesslen_] := {
  AbsoluteDashing[2], Line[{
    Offset[{UnitSize*loc, 0}, {0, 0}],
    Offset[{UnitSize*loc, UnitSize*(hmesslen)}, {0, 0}]
    }],
  AbsoluteThickness[2], AbsoluteDashing[UnitSize], Line[{
    Offset[{UnitSize*loc, UnitSize}, {0, 0}],
    Offset[{UnitSize*loc, UnitSize*(hmesslen)}, {0, 0}]
    }]
  }

HorizON[loc_, vmesslen_] := {
  AbsoluteDashing[2], Line[{
    Offset[{0, UnitSize*loc}, {0, 0}],
    Offset[{UnitSize*(vmesslen), UnitSize*loc}, {0, 0}]
    }],
  AbsoluteThickness[2], AbsoluteDashing[UnitSize], Line[{
    Offset[{0, UnitSize*loc}, {0, 0}],
    Offset[{UnitSize*(vmesslen), UnitSize*loc}, {0, 0}]
    }]
  }
HorizOFF[loc_, vmesslen_] := {
  AbsoluteDashing[2], Line[{
    Offset[{0, UnitSize*loc}, {0, 0}],
    Offset[{UnitSize*(vmesslen), UnitSize*loc}, {0, 0}]
    }],
  AbsoluteThickness[2], AbsoluteDashing[UnitSize], Line[{
    Offset[{UnitSize, UnitSize*loc}, {0, 0}],
    Offset[{UnitSize*(vmesslen), UnitSize*loc}, {0, 0}]
    }]
  }

ToBinary[mess_] := Flatten[IntegerDigits[ToCharacterCode[mess], 2, 8]]

Hitomezashi[hmess_, vmess_] := Graphics[{
   FaceForm[], EdgeForm[AbsoluteThickness[3]], 
   Rectangle[{0, 0}, 
    Offset[{UnitSize*Length[ToBinary[hmess]], 
      UnitSize*Length[ToBinary[vmess]]}, {0, 0}]],
   Table[
    If[
     ToBinary[hmess][[i]] == 1,
     VerticalON[
      i - 1,
      Length[ToBinary[vmess]]
      ],
     VerticalOFF[
      i - 1,
      Length[ToBinary[vmess]]
      ]],
    {i, 1, Length[ToBinary[hmess]]}]
   ,
   Table[
    If[
     ToBinary[vmess][[i]] == 1,
     HorizON[
      i - 1,
      Length[ToBinary[hmess]]
      ],
     HorizOFF[
      i - 1,
      Length[ToBinary[hmess]]]],
    {i, 1, Length[ToBinary[vmess]]}]
   
   }, PlotRange -> Full, 
  ImageSize -> {UnitSize*(Length[ToBinary[vmess]] + 1/4), 
    UnitSize*(Length[ToBinary[hmess]] + 1/4)}]

(*Messages defining bottom and up the side*)
bottom = "ACE";
side = "ACE";

(*Knitting Gauge to estimate final size*)
VertGauge = Quantity[6, IndependentUnit["Rows"]/("Inches")];
HoriGauge = Quantity[5, IndependentUnit["Stitches"]/("Inches")];

(*Calculations*)
Stitches = 
  Quantity[Length[ToBinary[bottom]], IndependentUnit["Stitches"]];
Rows = Quantity[Length[ToBinary[side]], IndependentUnit["Rows"]];
StringForm["Design is `1`/`2` wide, and `3`/`4` long", Stitches, 
 N[ Stitches/HoriGauge], Rows, N[Rows/VertGauge]]

(*Design Chart*)
Hitomezashi[bottom, side]
$\endgroup$
2
  • $\begingroup$ Did you use Mathematica to generate the top image? If so, could you provide the code for it? It might be easier to use those procedures to generate a colored map. $\endgroup$ Dec 8 '21 at 20:46
  • $\begingroup$ Yes, I did. it is a bit cobbled together, because I only started this this afternoon. See the edit. $\endgroup$ Dec 8 '21 at 20:48
11
$\begingroup$
(* Import image *)
img = Import["https://i.stack.imgur.com/8IBrW.png"];

(* Dilate to diagonally connect components *) 
imgB = Binarize[Dilation[img, .5], .9];

(* Find components *)
components = 
  DeleteSmallComponents[
   DeleteBorderComponents@
    MorphologicalComponents[imgB, CornerNeighbors -> False], 10];

(* Create a neighbouring graph *)
neighbors = 
  Sort /@ Flatten@(ComponentMeasurements[components, "Neighbors"]
       /. (a_ -> b_) :> Table[a \[UndirectedEdge] i, {i, b}]) // 
   DeleteDuplicates;
graph = UndirectedGraph[neighbors, VertexLabels -> Automatic];

(* Find a 2-coloring *)
coloring = First@ResourceFunction["FindProperColorings"][graph, 2];
rules = Dispatch@MapThread[#1 -> #2 &, {VertexList[graph], coloring}];

(* Combine images *)
ImageMultiply[img, 
 Colorize[components /. rules, 
  ColorRules -> {0 -> White, 1 -> LightBlue, 2 -> Pink}]]

Mathematica graphics

$\endgroup$
2
  • $\begingroup$ This is very nice! $\endgroup$
    – MarcoB
    Dec 8 '21 at 20:20
  • $\begingroup$ This seems to work very well, at least on things I have tested so far, and shows all the resource functions that I was wanting to find (graph/map coloring and region detection) $\endgroup$ Dec 8 '21 at 22:58
7
$\begingroup$

Starting with a "cleaner" image (e.g. without the dotted lines and higher resolution / no compression artifacts) would probably give better results, but this might give you a starting point:

img = <yourBWImagePastedHere>;
cropped = ImageCrop[img, {359, 359}];
Colorize@ ImageForestingComponents[cropped]

colorized image components

An alternative to colorize the image components with just two alternating colors:

Colorize[
 ImageForestingComponents[cropped],
 ColorRules -> {_?EvenQ -> White, _?OddQ -> Darker@Blue}
]

colorized with only two colors

$\endgroup$
1
$\begingroup$

The approach below didn't quite work out, because I can't quite get my coordinates to work out with yours. Specifically, if you resize the diagram created by this code in the notebook, the "filling" grows & shrinks while the grid lines stay the same size. I suspect this has something to do with the use of absolute values for the gridline coordinates and could be fixed, but unfortunately I don't have time to do so just now. I will try to explain the principles involved so you can revise the code to your satisfaction. If it's successful, it would mean not having to muck around with rasterization and component detection and such.

The code below generates a set of vector graphics that create the filling for the Hitomezashi. This is done by defining an array recursively as follows:

  • Row 1 (the bottom row) is defined by the running total of the horizontal seed string for the bottom.
  • Row $n$ is defined by taking row $n-1$ and adding the values $$ \begin{cases}(0,1,0,1,0,1,...) & v_n = 0 \\ (1,0,1,0,1,0,...) & v_n = 1 \end{cases}$$ where $v_n$ is the $n$th bit of the vertical seed string.

All addition is done mod 2.

This creates an array of zeroes and ones that gives the coloring of the hitomezashi cells. The rest is just generating a set of colored squares in two colors (color0 and color1) that lie "under" the grid lines. This is where I couldn't get things to work correctly.

filledHitomezashi[hmess_, vmess_] :=
 (
  colorrowbits[1] = Mod[Accumulate[ToBinary[hmess]], 2];
  colorrowbits[n_] := 
   colorrowbits[n] = 
    Mod[colorrowbits[n - 1] + ToBinary[vmess][[n]] + 
      Range[Length[ToBinary[hmess]]] + 1, 2];
  color0 = Blue;
  color1 = White;
  colorarray = 
   Table[
     colorrowbits[n], {n, Length[ToBinary[vmess]]}] /. {0 -> color0, 1 -> color1};
  squaresize = UnitSize;
  colorsquare[hloc_, vloc_] := {colorarray[[hloc, vloc]], 
    Rectangle[{hloc - 1, vloc - 1}*squaresize, {hloc, vloc}*squaresize]};
  Graphics[
   Join[
    Table[
     colorsquare[i, j], {i, Length[ToBinary[hmess]]}, {j, Length[ToBinary[vmess]]}],     {FaceForm[], 
 EdgeForm[AbsoluteThickness[3]], 
 Rectangle[{0, 0}, 
  Offset[{UnitSize*Length[ToBinary[hmess]], 
    UnitSize*Length[ToBinary[vmess]]}, {0, 0}]], 
 Table[
  If[ToBinary[hmess][[i]] == 1, 
   VerticalON[i - 1, Length[ToBinary[vmess]]], 
   VerticalOFF[i - 1, Length[ToBinary[vmess]]]], {i, 1, 
   Length[ToBinary[hmess]]}], 
 Table[
  If[ToBinary[vmess][[i]] == 1, 
   HorizON[i - 1, Length[ToBinary[hmess]]], 
   HorizOFF[i - 1, Length[ToBinary[hmess]]]], {i, 1, 
   Length[ToBinary[vmess]]}]}]])
filledHitomezashi[bottom, side]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.