CITE THIS NOTEBOOK: Difference map generative art by George Varnavides. Wolfram Community FEB 3 2023.
I find I keep coming back to this application https://community.wolfram.com/groups/-/m/t/2135869 every time I learn of a new optimization technique!
This time, it’s projection onto (non-)convex sets, e.g. the “difference map” algorithm which was previously used to solve Sudoku puzzles (which is of-course another problem amenable to Linear Programming). A slightly longer walk-through of the projections I used can be found here.
Hope you find watching the algorithm trying to climb out of a local minimum as mesmerizing as I do!

Target Image

In[]:=
Show[Image[obama=ImageData@ColorConvert[ImageResize[ImageCrop[ImageCrop[Import["https://upload.wikimedia.org/wikipedia/en/5/55/Barack_Obama_Hope_poster.jpg"],{225,235},Bottom],{225,225},Top],30],"Grayscale"]],ImageSize->300]
Out[]=
In[]:=
GraphicsRow[{Show[Image[obama],ImageSize->300],Show[createPict[tileDataGrayscale,arrayToKeepGS//Last],ImageSize300],Show[createPict[tileDataSmith,arrayToKeep//Last],ImageSize300]},ImageSize->900]
Out[]=

Dark Tiles

In[]:=
{darkCol,backgroundCol,lightCol}={Black,Gray,White};​​tileDataGrayscale=mkDarkTiles[backgroundCol,{darkCol,lightCol}];​​tileValuesGrayscale=First/@ColorConvert[tileDataGrayscale["tileColorValue"],"Grayscale"];​​tileDataGrayscale["allTilesImg"]
Out[]=

,
,
,
,
,
,
,
,
,
,
,
,
,
,
,

In[]:=
linearProgrammingSolution=partitionSolve[tileDataGrayscale,Image[obama],30];​​Show[createPict[tileDataGrayscale,linearProgrammingSolution],ImageSize300]
Out[]=
In[]:=
arrayGS=ConstantArray[N[RandomInteger[{0,1},Append[Dimensions[obama],Length[tileDataGrayscale["allTilesImg"]]]]],3];​​bestTilingErrorGS=100.;​​arrayToKeepGS={blockProjection[arrayGS//Mean].Range[Length[tileDataGrayscale["allTilesImg"]]]};
In[]:=
Do[​​​​arrayGS=differenceMapIteration[0.05][tileDataGrayscale["topologyH"],tileDataGrayscale["topologyV"],obama][0.125,tileValuesGrayscale][arrayGS];​​meanArrayGS=arrayGS//Mean;​​​​horizontalCostGS=horizontalTilingCost[tileDataGrayscale][meanArrayGS]*100;​​verticalCostGS=verticalTilingCost[tileDataGrayscale][meanArrayGS]*100;​​pixelValuesCostGS=pixelCost[obama,tileValuesGrayscale][meanArrayGS]*100;​​​​If[​​Mean[{horizontalCostGS,verticalCostGS}]<bestTilingErrorGS,​​bestTilingErrorGS=Mean[{horizontalCostGS,verticalCostGS}];​​bestArrayGS=meanArrayGS;​​];​​​​If[Mod[i,20]==0,​​AppendTo[arrayToKeepGS,blockProjection[meanArrayGS].Range[Length[tileDataGrayscale["allTilesImg"]]]]];​​​​errorGS=StringTemplate["Best Tiling Cost: `4` %-- Current Horizontal Tiling Cost: `1` %-- Current Vertical Tiling Cost: `2` %-- Current Pixel Values Cost: `3` %"][horizontalCostGS,verticalCostGS,pixelValuesCostGS,bestTilingErrorGS];​​,{i,10000}​​]
In[]:=
(*Dynamic[errorGS]*)
(*Dynamic[visualizeArray[tileDataGrayscale]/@{bestArrayGS,meanArrayGS}]*)
In[]:=
arrayToKeepGS=Import["dark-tiles_obama30x30_rrr05.mx"];
In[]:=
Export["dark-tiles_obama30x30_rrr05.mx",arrayToKeepGS]
Out[]=
dark-tiles_obama30x30_rrr05.mx
In[]:=
framesGS=Show[createPict[tileDataGrayscale,#],ImageSize300]&/@arrayToKeepGS;
In[]:=
Export["dark-tiles_obama30x30_rrr05.gif",framesGS]
Out[]=
dark-tiles_obama30x30_rrr05.gif
In[]:=
Iconize[arrayGS,"dark-tiles_obama30x30_rrr05_final-array"]
Out[]=
dark-tiles_obama30x30_rrr05_final-array

Smith Tiles

In[]:=
tileDataSmith=mkSmithTiles[{Black,Gray,White}];​​tileValuesSmith=First/@ColorConvert[tileDataSmith["tileColorValue"],"Grayscale"];​​tileDataSmith["allTilesImg"]
Out[]=

,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,

In[]:=
linearProgrammingSolutionSmith=partitionSolve[tileDataSmith,Image[obama],30];​​Show[createPict[tileDataSmith,linearProgrammingSolutionSmith],ImageSize300]
Out[]=

Tiling Initialization Functions

Projection Initialization Functions