// cannot process close to the edge yet private static img upscale( img pic, UInt16?scoring_function_index = null, byte?scale = null, byte?replacement_size_ish = null, byte?verbosity = null) { if (verbosity == null) { verbosity = 3; } if (verbosity > 0) { writer.write_then_inc("upscale"); } if (scoring_function_index == null) { scoring_function_index = 0; } if (scale == null) { scale = 5; } if (replacement_size_ish == null) { replacement_size_ish = 3; } if (verbosity > 1) { writer.write("scoring function index: " + scoring_function_index); } var scoring_function = UpScaley.score.get((UInt16)scoring_function_index); if (verbosity > 1) { writer.write("image width: " + pic.RGB.GetLength(0)); writer.write("image height: " + pic.RGB.GetLength(1)); writer.write("Scale base image by " + scale + " times."); } if (scale == 0) { throw new ArgumentException("Scale cannot be 0."); } var Base = expand.bicubic(pic, (float)scale); var replacement_size = (UInt16)(scale * replacement_size_ish); if (verbosity > 1) { writer.write("base width: " + Base.RGB.GetLength(0)); writer.write("base height: " + Base.RGB.GetLength(1)); writer.write("style width: " + pic.RGB.GetLength(0)); writer.write("style height: " + pic.RGB.GetLength(1)); writer.write("replacement size: " + scale + " * " + replacement_size_ish + " = " + replacement_size); } var result = style_transfer.trans(Base, pic, scoring_function, replacement_size, (byte)verbosity); if (verbosity > 0) { writer.dec(); } return(result); }
// code by Tim private static RGB bicubic_RGB_interpolator(img image, Int32 x_pos, Int32 y_pos, float x_frac, float y_frac) { byte i, j; Int16 interpolated_value; var interpolator_patch = new float[4, 4]; RGB return_value = new RGB(); for (byte c = 0; c < 3; c++) { for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { interpolator_patch[i, j] = image[x_pos + i, y_pos + j][c]; } } // Clamping to prevent bad pixels as cubic interpolation can yield values out of range interpolated_value = (Int16)bicubic_interpolator(interpolator_patch, x_frac, y_frac); if (0 == (0xff00 & interpolated_value)) { return_value[c] = (byte)interpolated_value; } else if (0 > interpolated_value) { return_value[c] = 0; } else { return_value[c] = 0xff; } } return(return_value); }
// code by Tim public static img expand(img n, float scale) { Int32 x, y, x_pos, y_pos, n_width = n.RGB.GetLength(0), n_height = n.RGB.GetLength(1), result_width = (Int32)(n_width * scale), result_height = (Int32)(n_height * scale); var result = new img(result_width, result_height); var image = new img(n_width + 2, n_height + 2); // arrays run from 0 to n-1, so if we want ends to match, we need to scale appropriately float scale_inv_width = (n_width - 1f) / (result_width - 1f); float scale_inv_height = (n_height - 1f) / (result_height - 1f); // avoid sampling off edge of image float scale_inv = 0.999999f * ((scale_inv_width < scale_inv_height) ? scale_inv_width : scale_inv_height); float x_sample, y_sample, x_frac, y_frac; for (x = 0; x < n_width; x++) { image[x + 1, 0] = n[x, 0]; for (y = 0; y < n_height; y++) { image[x + 1, y + 1] = n[x, y]; } image[x + 1, n_height + 1] = n[x, n_height - 1]; } for (y = 0; y < n_height + 2; y++) { image[0, y] = image[1, y]; image[n_width + 1, y] = image[n_width, y]; } for (x = 0; x < result_width; x++) { x_sample = (float)(x * scale_inv); x_pos = (Int32)x_sample; x_frac = x_sample - (float)x_pos; for (y = 0; y < result_height; y++) { y_sample = (float)(y * scale_inv); y_pos = (Int32)y_sample; y_frac = y_sample - (float)y_pos; result[x, y] = bicubic_RGB_interpolator(image, x_pos, y_pos, x_frac, y_frac); } } return(result); }
public static img nearest(img n, float scale) { Int32 x, y, n_width = n.RGB.GetLength(0), n_height = n.RGB.GetLength(1), result_width = (Int32)(n_width * scale), result_height = (Int32)(n_height * scale); var result = new img(result_width, result_height); float scale2 = 1f / scale; for (x = 0; x < result_width; x++) { for (y = 0; y < result_height; y++) { result[x, y] = n[(Int32)(x * scale2), (Int32)(y * scale2)]; } } return(result); }
public static img bicubic(img n, float scale) { return(bicubic2.expand(n, scale)); }
static void Main(string[] args) { writer.write("reading args"); var arg_count = args.Length; string filename; if (arg_count > 0) { filename = args[0]; } else { throw new ArgumentException("Filename required as command-line argument."); } UInt16?scoring_function_index = null; byte? scale = null, replacement_size_ish = null; if (arg_count > 1) { if (args[1] != "_") { scoring_function_index = Convert.ToUInt16(args[1]); } if (arg_count > 2) { if (args[2] != "_") { scale = Convert.ToByte(args[2]); } if (arg_count > 3) { if (arg_count == 4) { if (args[3] != "_") { replacement_size_ish = Convert.ToByte(args[3]); } } else { throw new ArgumentException("too many arguments, 4 max"); } } } } writer.write("precomputing"); init(); var source = "../../../pics/"; writer.write("loading pic"); var pic = new System.Drawing.Bitmap(System.Drawing.Image.FromFile(source + filename)); var pic2 = new img(pic); var result = upscale(pic2, scoring_function_index, scale, replacement_size_ish, 4).to_bitmap(); writer.write("saving result"); result.Save(source + "result.png", System.Drawing.Imaging.ImageFormat.Png); writer.write("clearing up"); pic.Dispose(); result.Dispose(); writer.write("done"); Console.ReadLine(); }
// cannot process close to the edge yet public static img trans(img Base, img style, Func <img, img, Int32, Int32, Int32, Int32, Int32, float> score, UInt16 replacement_size, byte verbosity = 3) { if (verbosity > 4) { throw new ArgumentException("4 is the maximum verbosity"); } if (score == null) { throw new ArgumentException("A scoring function is required."); } Int32 replacement_size2 = replacement_size; if (replacement_size2 % 2 == 0) { throw new ArgumentException("replacement_size must be an odd number"); } UInt32 denominator = (UInt32)(verbosity > 3 ? 1000000 : 1000); byte numerator = 1, least_denominator = verbosity > 3 ? (byte)100 : (byte)20, cycle_pos = 0, best_deviations_count = 5; Int32 base_x, base_y, base_and_result_width = Base.RGB.GetLength(0), base_and_result_height = Base.RGB.GetLength(1), style_width = style.RGB.GetLength(0), style_height = style.RGB.GetLength(1), base_and_result_width_subtract_replacment_size = base_and_result_width - replacement_size2, base_and_result_height_subtract_replacment_size = base_and_result_height - replacement_size2, style_width_subtract_replacment_size = style_width - replacement_size2, style_height_subtract_replacment_size = style_height - replacement_size2, half_replacement_size = (Int32)(replacement_size2 * 0.5f); var result = new img(base_and_result_width, base_and_result_height); UInt64 pixel_count = 0, expected_pixels = (UInt64)(base_and_result_width_subtract_replacment_size * base_and_result_height_subtract_replacment_size); if (verbosity > 0) { writer.write_then_inc("style transfer"); } var region_comparisons = (UInt64)base_and_result_width_subtract_replacment_size * (UInt64)base_and_result_height_subtract_replacment_size * (UInt64)style_width_subtract_replacment_size * (UInt64)style_height_subtract_replacment_size; if (verbosity > 1) { writer.write("replacement_size: " + replacement_size); writer.write("The load is about " + (UInt64)Math.Round(region_comparisons * (UInt64)replacement_size * (UInt64)replacement_size * 0.000000001f) + " billion pixel pair comparisons."); } if (verbosity > 2) { writer.write("Or (" + (UInt64)Math.Round(region_comparisons * 0.000001f) + " million region comparisons) or (" + (UInt64)Math.Round(expected_pixels * 0.001f) + " thousand pixels of base/result resolved (" + expected_pixels + "))."); writer.write("base and result width: " + base_and_result_width); writer.write("base and result height: " + base_and_result_height); writer.write("style width: " + style_width); writer.write("style height: " + style_height); writer.write("base and result width subtract replacment size: " + base_and_result_width_subtract_replacment_size); writer.write("base and result height subtract replacment size: " + base_and_result_height_subtract_replacment_size); writer.write("style width subtract replacment size: " + style_width_subtract_replacment_size); writer.write("style height subtract replacment size: " + style_height_subtract_replacment_size); } for (base_x = 0; base_x < base_and_result_width_subtract_replacment_size; base_x++) { for (base_y = 0; base_y < base_and_result_height_subtract_replacment_size; base_y++) { if (verbosity > 1) { while ((float)pixel_count / expected_pixels > (float)numerator / denominator) { writer.write("" + numerator + " / " + denominator); if (denominator == least_denominator) { numerator++; } else { denominator = (UInt32)(denominator * (cycle_pos % 3 == 1 ? 0.4f : 0.5f)); } cycle_pos++; } } pixel_count++; var best_deviations_rgb = new RGB[style_width_subtract_replacment_size, best_deviations_count]; var best_deviations = new float[style_width_subtract_replacment_size, best_deviations_count]; { byte pos, pos2; for (pos = 0; pos < style_width_subtract_replacment_size; pos++) { for (pos2 = 0; pos2 < best_deviations_count; pos2++) { best_deviations[pos, pos2] = float.MaxValue; } } } Int32 pass = 0, worst_x = 0; var best_columns = new Int32[best_deviations_count]; var best_columns_value = new float[best_deviations_count]; var mut = new Mutex(); // OpenCL/CUDA (or GLSL even) here, proposal 'a'. System.Threading.Tasks.Parallel.For(0, style_width_subtract_replacment_size, style_x => { // GPU proposal 'b'. byte pos; float best_value = float.MaxValue; Int32 worst_pos = 0; for (Int32 style_y = 0; style_y < style_height_subtract_replacment_size; style_y++) { // GPU proposal 'c', see col_sim_inc5 in Score.cs for proposal 'd'. float deviation = score(Base, style, base_x, base_y, style_x, style_y, replacement_size2); // end 'c' pass++; if (deviation <= best_deviations[style_x, worst_pos]) { best_deviations_rgb[style_x, worst_pos] = style[style_x + half_replacement_size, style_y + half_replacement_size]; best_deviations[style_x, worst_pos] = deviation; for (pos = 0; pos < best_deviations_count; pos++) { if (best_deviations[style_x, pos] > best_deviations[style_x, worst_pos]) { worst_pos = pos; } } // Need the best value, as the best columns are those containing by the best matches. if (deviation < best_value) { best_value = deviation; } } } // end 'b' // code by Tim { mut.WaitOne(); // The n best elements are contained in the n best columns, as ordered by best match in that column. if (best_value < best_columns_value[worst_x]) { // Replace the worst x with a better one (mutex avoids race condition). best_columns[worst_x] = style_x; best_columns_value[worst_x] = best_value; // Find the new worst x. for (pos = 0; pos < best_deviations_count; pos++) { if (best_columns_value[pos] > best_columns_value[worst_x]) { worst_x = pos; } } } mut.ReleaseMutex(); } }); // end 'a' result[base_x + half_replacement_size, base_y + half_replacement_size] = RGB_from_candidate_matches(best_deviations, best_deviations_rgb, best_columns); } } if (verbosity > 0) { writer.write("Pixels of base/result calculated: " + pixel_count); if (pixel_count < expected_pixels) { writer.write("ERROR: Skipped some pixels?"); } if (pixel_count > expected_pixels) { writer.write("ERROR: Duplicated pixels?"); } writer.dec(); } return(result); }