/// /// Load a bitmap and create ASCII art from it using linear optimization /// /// private void ASCIILinearOptimization(int blockSize) { char[] tiles = new char[] { '■', '@', '#', '?', '!', 'x', '-', ',', '.', ' ' }; double[] tilesWeight = new double[] { 1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.3, 0.2, 0.1, 0.0 }; double[,] image = GetBlockBrightness("./Input/Lena.jpg", blockSize); CpModel model = new CpModel(); // variables Dictionary variableSet = new Dictionary(); // cost function LinearExprBuilder cost = LinearExpr.NewBuilder(); // for each block for (int i = 0; i < image.GetLength(0); i++) { for (int j = 0; j < image.GetLength(1); j++) { // create variables for each combination block-character List forOneSquare = new List(); for (int c = 0; c < tiles.Length; c++) { string varName = "x_" + i + "_" + j + "_" + c; IntVar xijc = model.NewIntVar(0, 1, varName); variableSet.Add(varName, xijc); forOneSquare.Add(xijc); // weight has to be integer !! long weight = (long)(100 * Math.Abs(image[i, j] - tilesWeight[c])); // add to cost function cost.AddTerm(xijc, weight); } // for each square we can pick only one tile model.Add(LinearExpr.Sum(forOneSquare) == 1); // = one constraint per block } } // minimize cost function with given constraints model.Minimize(cost); CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); // if successfull solving print result if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { // check each variable for (int i = 0; i < image.GetLength(0); i++) { for (int j = 0; j < image.GetLength(1); j++) { for (int c = 0; c < tiles.Length; c++) { string varName = "x_" + i + "_" + j + "_" + c; // if variable is true, print corresponding character if (solver.Value(variableSet[varName]) == 1) Console.Write(tiles[c]); } } Console.WriteLine(); } } else { Console.WriteLine("No solution found."); } }