thomaseding commited on
Commit
801759b
·
1 Parent(s): a1a2d25

Add some debug code

Browse files
Files changed (2) hide show
  1. README.md +1 -1
  2. controlled_downscale.py +58 -6
README.md CHANGED
@@ -46,7 +46,7 @@ Q: Why is this needed? Can't I use a post-processor to downscale the image?
46
 
47
  Q: Is there special A1111 user-interface integration?
48
 
49
- A: Yes... but not yet merged into the standard ControlNet extension's code. See https://github.com/thomaseding/sd-webui-controlnet/tree/teding/feat/pixelnet if you want to integrate the changes yourself in the meantime.
50
 
51
  A: From my experience SD has a hard time creating genuine pixel art (even with dedicated base models and loras), where it has a mismatch of logical pixel sizes, smooth curves, etc. What appears to be a straight line at a glance, might bend around. This can cause post-processors to create artifacts based on quantization rounding a pixel to a position one pixel off in some direction. This model is intended to help fix that.
52
 
 
46
 
47
  Q: Is there special A1111 user-interface integration?
48
 
49
+ A: Yes... but not yet merged into the standard ControlNet extension's code. See (https://civitai.com/posts/371477) if you want to integrate the changes yourself in the meantime.
50
 
51
  A: From my experience SD has a hard time creating genuine pixel art (even with dedicated base models and loras), where it has a mismatch of logical pixel sizes, smooth curves, etc. What appears to be a straight line at a glance, might bend around. This can cause post-processors to create artifacts based on quantization rounding a pixel to a position one pixel off in some direction. This model is intended to help fix that.
52
 
controlled_downscale.py CHANGED
@@ -59,6 +59,48 @@ class ExtractedBoxes:
59
  down = back.down_pos()
60
  return (down[0] + 1, down[1] + 1)
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  def average_box_dimensions(boxes: List[DownBox]) -> Dim:
64
  assert len(boxes) > 0
@@ -235,7 +277,7 @@ def str2bool(value) -> bool:
235
  raise argparse.ArgumentTypeError("Boolean value expected.")
236
 
237
 
238
- def controlled_downscale(*, control_path: str, input_path: str, output_downscaled_path: Optional[str], output_quantized_path: Optional[str], sample_radius: Optional[int], downsampler: Image.Resampling, trim_cropped_edges: bool) -> None:
239
  """
240
  Downsample and rescale an image.
241
 
@@ -246,6 +288,8 @@ def controlled_downscale(*, control_path: str, input_path: str, output_downscale
246
  :param sample_radius: Radius for sampling (Manhattan distance).
247
  :param downsampler: Downsampler to use.
248
  :param trim_cropped_edges: Drop mapped checker grid elements that are cropped in the control image.
 
 
249
  """
250
  if not output_downscaled_path and not output_quantized_path:
251
  raise ValueError("At least one of output_up and output_down must be specified.")
@@ -262,6 +306,10 @@ def controlled_downscale(*, control_path: str, input_path: str, output_downscale
262
  quantized_image = ImageRef(Image.new("RGB", input_image.size))
263
 
264
  extracted_boxes = extract_boxes(control_image)
 
 
 
 
265
 
266
  if output_downscaled_path:
267
  downscaled_image = ImageRef(Image.new("RGB", extracted_boxes.down_dimensions()))
@@ -279,13 +327,15 @@ def controlled_downscale(*, control_path: str, input_path: str, output_downscale
279
 
280
  def main(cli_args: List[str]) -> None:
281
  parser = argparse.ArgumentParser(description="Downsample and rescale image.")
282
- parser.add_argument("--control", required=True, help="Path to control image.")
283
- parser.add_argument("--input", required=True, help="Path to input image.")
284
- parser.add_argument("--output-downscaled", help="Path to save the output downscaled image.")
285
- parser.add_argument("--output-quantized", help="Path to save the output quantized image (downscaled and then upscaled to the original size).")
286
  parser.add_argument("--sample-radius", type=int, default=None, help="Radius for sampling (Manhattan distance).")
287
  parser.add_argument("--downsampler", choices=["box", "bilinear", "bicubic", "hamming", "lanczos"], default="box", help="Downsampler to use.")
288
  parser.add_argument("--trim-cropped-edges", type=str2bool, default=False, help="Drop mapped checker grid elements that are cropped in the control image.")
 
 
289
 
290
  args = parser.parse_args(cli_args)
291
  downsampler = Image.Resampling[args.downsampler.upper()]
@@ -297,7 +347,9 @@ def main(cli_args: List[str]) -> None:
297
  output_quantized_path=args.output_quantized,
298
  sample_radius=args.sample_radius,
299
  downsampler=downsampler,
300
- trim_cropped_edges=args.trim_cropped_edges
 
 
301
  )
302
 
303
 
 
59
  down = back.down_pos()
60
  return (down[0] + 1, down[1] + 1)
61
 
62
+ def full_dimensions(self) -> Dim:
63
+ if len(self._boxes) == 0:
64
+ return (0, 0)
65
+ back = self._boxes[-1]
66
+ max = back.max()
67
+ return (max[0] + 1, max[1] + 1)
68
+
69
+ def to_colored_checkers(self, *, full=True) -> Image.Image:
70
+ if full:
71
+ width, height = self.full_dimensions()
72
+ else:
73
+ width, height = self.down_dimensions()
74
+ if width == 0 or height == 0:
75
+ return Image.new("RGB", (0, 0))
76
+ image = Image.new("RGB", (width, height))
77
+ colors = [
78
+ (255, 255, 255),
79
+ (0, 0, 0),
80
+ (255, 0, 0),
81
+ (255, 127, 0),
82
+ (255, 255, 0),
83
+ (0, 255, 0),
84
+ (0, 0, 255),
85
+ (75, 0, 130),
86
+ (148, 0, 211),
87
+ (255, 0, 255),
88
+ ]
89
+ colorsMax = len(colors)
90
+ currColor = 0
91
+ for box in self._boxes:
92
+ color = colors[currColor]
93
+ currColor = (currColor + 1) % colorsMax
94
+ if full:
95
+ dim = box.dimensions()
96
+ pos = box.min()
97
+ else:
98
+ dim = (1, 1)
99
+ pos = box.down_pos()
100
+ subImage = Image.new("RGB", dim, color)
101
+ image.paste(subImage, pos)
102
+ return image
103
+
104
 
105
  def average_box_dimensions(boxes: List[DownBox]) -> Dim:
106
  assert len(boxes) > 0
 
277
  raise argparse.ArgumentTypeError("Boolean value expected.")
278
 
279
 
280
+ def controlled_downscale(*, control_path: str, input_path: str, output_downscaled_path: Optional[str], output_quantized_path: Optional[str], sample_radius: Optional[int], downsampler: Image.Resampling, trim_cropped_edges: bool, output_colorized_full_path: Optional[str], output_colorized_down_path: Optional[str]) -> None:
281
  """
282
  Downsample and rescale an image.
283
 
 
288
  :param sample_radius: Radius for sampling (Manhattan distance).
289
  :param downsampler: Downsampler to use.
290
  :param trim_cropped_edges: Drop mapped checker grid elements that are cropped in the control image.
291
+ :param output_colorized_full_path: Colorize the full checker image to debug the checker parsing.
292
+ :param output_colorized_down_path: Colorize the downscaled checker image to debug the checker parsing.
293
  """
294
  if not output_downscaled_path and not output_quantized_path:
295
  raise ValueError("At least one of output_up and output_down must be specified.")
 
306
  quantized_image = ImageRef(Image.new("RGB", input_image.size))
307
 
308
  extracted_boxes = extract_boxes(control_image)
309
+ if output_colorized_full_path:
310
+ extracted_boxes.to_colored_checkers(full=True).save(output_colorized_full_path)
311
+ if output_colorized_down_path:
312
+ extracted_boxes.to_colored_checkers(full=False).save(output_colorized_down_path)
313
 
314
  if output_downscaled_path:
315
  downscaled_image = ImageRef(Image.new("RGB", extracted_boxes.down_dimensions()))
 
327
 
328
  def main(cli_args: List[str]) -> None:
329
  parser = argparse.ArgumentParser(description="Downsample and rescale image.")
330
+ parser.add_argument("--control", type=str, required=True, help="Path to control image.")
331
+ parser.add_argument("--input", type=str, required=True, help="Path to input image.")
332
+ parser.add_argument("--output-downscaled", type=str, help="Path to save the output downscaled image.")
333
+ parser.add_argument("--output-quantized", type=str, help="Path to save the output quantized image (downscaled and then upscaled to the original size).")
334
  parser.add_argument("--sample-radius", type=int, default=None, help="Radius for sampling (Manhattan distance).")
335
  parser.add_argument("--downsampler", choices=["box", "bilinear", "bicubic", "hamming", "lanczos"], default="box", help="Downsampler to use.")
336
  parser.add_argument("--trim-cropped-edges", type=str2bool, default=False, help="Drop mapped checker grid elements that are cropped in the control image.")
337
+ parser.add_argument("--output-colorized-full", type=str, help="Colorize the full checker image to debug the checker parsing.")
338
+ parser.add_argument("--output-colorized-down", type=str, help="Colorize the downscaled checker image to debug the checker parsing.")
339
 
340
  args = parser.parse_args(cli_args)
341
  downsampler = Image.Resampling[args.downsampler.upper()]
 
347
  output_quantized_path=args.output_quantized,
348
  sample_radius=args.sample_radius,
349
  downsampler=downsampler,
350
+ trim_cropped_edges=args.trim_cropped_edges,
351
+ output_colorized_full_path=args.output_colorized_full,
352
+ output_colorized_down_path=args.output_colorized_down,
353
  )
354
 
355