def test_single_image_correct_size(self): grid = figuregen.Grid(1, 1) img_blue = np.tile([x / 255 for x in colors[2]], (32, 64, 1)) grid[0, 0].image = figuregen.PNG(img_blue) backend = TikzBackend() sz = backend.compute_aligned_sizes([grid], 13) self.assertAlmostEqual(sz[0][0].width_mm, 13) self.assertAlmostEqual(sz[0][0].height_mm, 13 / 2) self.assertAlmostEqual(sz[0][1].width_mm, 13) self.assertAlmostEqual(sz[0][1].height_mm, 13 / 2)
def test_image_with_title_correct_pos(self): grid = figuregen.Grid(1, 1) img_blue = np.tile([x / 255 for x in colors[2]], (32, 64, 1)) grid[0, 0].image = figuregen.PNG(img_blue) grid.set_col_titles("top", ["hi there"]) grid.layout.set_col_titles("top", 5, 0.5) backend = TikzBackend() sz = backend.compute_aligned_sizes([grid], 13) # grid size should include title self.assertAlmostEqual(sz[0][0].width_mm, 13) self.assertAlmostEqual(sz[0][0].height_mm, 13 / 2 + 5.5) # image size should be same as without title self.assertAlmostEqual(sz[0][1].width_mm, 13) self.assertAlmostEqual(sz[0][1].height_mm, 13 / 2) imgpos = calc.image_pos(grid, sz[0][1], 0, 0) self.assertAlmostEqual(imgpos[0], 5.5) self.assertAlmostEqual(imgpos[1], 0)
import figuregen import numpy as np import single_module # generate test images blue = np.tile([0.2, 0.3, 0.9], (32, 32, 1)) yellow = np.tile([0.9, 0.8, 0.2], (32, 32, 1)) # load the two images images = [figuregen.PNG(blue), figuregen.PNG(yellow)] # ---- Grid Module ---- grid0 = figuregen.Grid(1, 1) grid0.get_layout().set_padding(right=0.5) e0 = grid0.get_element(0, 0).set_image(images[1]) e0.set_frame(0.3, [0, 0, 0]) e0.set_marker(pos=[2, 12], size=[10, 10], color=[155, 155, 155], linewidth_pt=0.6) e0.set_marker(pos=[15, 1], size=[10, 15], color=[186, 98, 82], linewidth_pt=0.6) # ---- Grid Module ---- grid1 = figuregen.Grid(2, 2) layout1 = grid1.get_layout() layout1.set_padding(top=0.2, right=0.5) # fill grid with image data
import os factors = [ ("10", "Results-x10"), ("50", "Results-x50"), ("100", "Results-x100"), ] scene_folder = "IndirectRoom" def tonemap(img): return figuregen.JPEG(util.image.lin_to_srgb(img), quality=80) grid = figuregen.Grid(num_cols=3, num_rows=1) label_params = { "width_mm": 20, "height_mm": 10, "fontsize": 8, "offset_mm": [0.0, 0.0], "txt_padding_mm": 1, "bg_color": None, "txt_color": [255, 255, 255], } # crop = util.image.Cropbox(top=60, left=730, height=48, width=64, scale=10) crop = util.image.Cropbox(top=300, left=50, height=48, width=64, scale=10) i = 0
ddx.append(x[i]) ddy.append(y[i]) # * x[i]) rmin = y[i] return (ddx, ddy) def get_plot_data(scene, method_list): return [ load_error(scene, method, metric='MRSE*', clip=True) for method in method_list[:-1] ] # ---------- REFERENCE Module ---------- ref_grid = figuregen.Grid(1, 1) ref_img = get_image(scene[idx], seconds[idx]) reference = ref_grid.get_element(0, 0).set_image(figuregen.PNG(ref_img)) # marker for crop in crops[idx]: reference.set_marker(pos=crop.get_marker_pos(), size=crop.get_marker_size(), color=[242, 113, 0], linewidth_pt=0.6) # titles ref_grid.set_title('south', scene[idx].replace('-', ' ').title()) # layout ref_layout = ref_grid.get_layout().set_padding(bottom=0.1, right=0.5) ref_layout.set_title('south', field_size_mm=7., offset_mm=0.5, fontsize=8)
img_test = figuregen.HTML(htmlfile, aspect_ratio=0.3) else: # PDF test figuregen.figure(vertical_stack.v_grids, width_cm=15., filename='v-stacked.pdf') pdffile = os.path.abspath('v-stacked.pdf') img_test = figuregen.PDF(pdffile) images = [ img_test, img_png, ] # ---- GRIDS ---- grid = figuregen.Grid(1, 1) grid.get_layout().set_padding(right=1.5, bottom=1.0) e1 = grid.get_element(0, 0).set_image(images[0]) grid2 = figuregen.Grid(1, 1) e2 = grid2.get_element(0, 0).set_image(images[1]) grid2.get_layout().set_padding(bottom=1.0) all_grids = [[grid, grid], [grid, grid2]] if __name__ == "__main__": if test_html: figuregen.figure(all_grids, width_cm=28., filename='figure_in_figure.html') else:
def make_row(method_name, title, mark_strata=False, problem_range=None, show_titles=False): with open(f"data/{method_name}.json") as f: data = json.load(f) grid = figuregen.Grid(num_cols=6, num_rows=1) xticks = [0, 1] colors = [ [68, 104, 167], [244, 146, 42], [211, 95, 85], [89, 151, 223], [138, 74, 201], ] # Add the problem overview plot = figuregen.PgfLinePlot(aspect_ratio=0.9, data=[ (data["PdfA"]["X"], data["PdfA"]["Y"]), (data["PdfB"]["X"], data["PdfB"]["Y"]), (data["Integrand"]["X"], data["Integrand"]["Y"]) ]) plot.set_font(fontsize_pt=7) plot.set_linewidth(plot_line_pt=1) plot.set_axis_properties("x", xticks, range=[0, 1.1], use_log_scale=False) plot.set_axis_properties("y", [], range=problem_range, use_log_scale=False) plot.set_padding(left_mm=1, bottom_mm=3) plot.set_colors(colors) if mark_strata: marker_width = 0.5 marker_color = [100, 100, 100] marker_style = [1] plot.set_v_line(0.25, marker_color, marker_style, marker_width) plot.set_v_line(0.50, marker_color, marker_style, marker_width) plot.set_v_line(0.75, marker_color, marker_style, marker_width) plot.set_v_line(1.00, marker_color, marker_style, marker_width) grid.get_element(0, 0).set_image(plot) # Add the different methods variance_captions = [""] def plot_method(name, idx, crop=True): plot = figuregen.PgfLinePlot(aspect_ratio=0.9, data=[ (data[name]["WeightA"]["X"], data[name]["WeightA"]["Y"]), (data[name]["WeightB"]["X"], data[name]["WeightB"]["Y"]) ]) plot.set_font(fontsize_pt=7) plot.set_linewidth(plot_line_pt=1) plot.set_padding(left_mm=6 if not crop else 3, bottom_mm=3) plot.set_colors(colors) if mark_strata: plot.set_v_line(0.25, marker_color, marker_style, marker_width) plot.set_v_line(0.50, marker_color, marker_style, marker_width) plot.set_v_line(0.75, marker_color, marker_style, marker_width) plot.set_v_line(1.00, marker_color, marker_style, marker_width) if not crop: max_val_a = np.max(data[name]["WeightA"]["Y"]) min_val_a = np.min(data[name]["WeightA"]["Y"]) max_val_b = np.max(data[name]["WeightB"]["Y"]) min_val_b = np.min(data[name]["WeightB"]["Y"]) max_val = max(max_val_a, max_val_b) min_val = min(min_val_a, min_val_b) lo_tick = int(min_val / 10) * 10 hi_tick = int(max_val / 10) * 10 if hi_tick == 0: hi_tick = 1 if lo_tick == 0: lo_tick = -1 else: min_val = 0 max_val = 1 lo_tick = 0 hi_tick = 1 range = [min_val * 1.15, max_val * 1.15] plot.set_axis_properties("x", xticks, range=[0, 1.15], use_log_scale=False) plot.set_axis_properties("y", [lo_tick, hi_tick], range=range, use_log_scale=False) grid.get_element(0, idx).set_image(plot) variance_captions.append(f"Variance: {data[name]['Variance']:.4f}") plot_method("Average", 1) plot_method("RecipVar", 2) plot_method("Balance", 3) plot_method("VarAware", 4) plot_method("Optimal", 5, False) # Add titles grid.set_row_titles("left", [title]) grid.get_layout().set_row_titles("left", 3, offset_mm=0.5, fontsize=8) if show_titles: grid.set_col_titles("bottom", [ "a) Integrand and densities", "b) Average", "c) Opt. const.", "d) Balance heuristic", "e) Var-aware", "f) Optimal weights" ]) grid.get_layout().set_col_titles("bottom", 6, offset_mm=0.5, fontsize=8) grid.set_col_titles("top", variance_captions) grid.get_layout().set_col_titles("top", 3, offset_mm=0.5, fontsize=8) # Add space between the rows (needs to be removed for the last one) grid.get_layout().set_padding(bottom=2) return [grid]
weights=[0.5, 1.0]) ], [ image.SplitImage([img_yellow, img_orange], weights=[1.0, 1.0], degree=30), image.SplitImage([img_yellow, img_l_blue, img_blue], vertical=False, weights=[1, 2, 3], degree=0), ]] # ---------- Simple Grid with SplitImages ---------- n_rows = 2 top_cols = 2 top_grid = figuregen.Grid(num_rows=n_rows, num_cols=top_cols) # fill grid with image data for row in range(n_rows): for col in range(top_cols): s_img = images[row][col] raw_img = figuregen.PNG(s_img.get_image()) e = top_grid.get_element(row, col).set_image(raw_img) e.draw_lines(s_img.get_start_positions(), s_img.get_end_positions(), linewidth_pt=0.5, color=[0, 0, 0]) top_grid.set_col_titles('top', ['Horizontal Split', 'Vertical Split', 'C)', 'D)'])
import figuregen import numpy as np img_blue = np.tile([x / 255 for x in [94, 163, 188]], (32, 64, 1)) img_yellow = np.tile([x / 255 for x in [232, 181, 88]], (32, 64, 1)) grid = figuregen.Grid(1, 2) grid[0, 0].set_image(figuregen.JPEG(img_blue)) grid[0, 1].set_image(figuregen.JPEG(img_yellow)) figuregen.horizontal_figure([grid], 8, "jpeg_export.pdf") figuregen.horizontal_figure([grid], 8, "jpeg_export.pptx") figuregen.horizontal_figure([grid], 8, "jpeg_export.html") grid = figuregen.Grid(1, 2) grid[0, 0].set_image(figuregen.PNG(img_blue)) grid[0, 1].set_image(figuregen.PNG(img_yellow)) figuregen.horizontal_figure([grid], 8, "png_export.pdf") figuregen.horizontal_figure([grid], 8, "png_export.pptx") figuregen.horizontal_figure([grid], 8, "png_export.html")
def make_figure(methods, scene_folder, cropA, cropB, scene_name, exposure=0, show_method_names=True, times=None, outline_color=[10, 10, 10], text_color=[250, 250, 250]): method_images = [ simpleimageio.read(os.path.join(scene_folder, folder, "render.exr")) for _, folder in methods ] reference_image = simpleimageio.read(os.path.join(scene_folder, "reference.exr"))[:,:,:3] # Remove NaN and Inf values to not pollute the error (the right way would be to fix them ...) for m in method_images: mask = np.ma.masked_invalid(m).mask m[mask] = 0 mask = np.ma.masked_invalid(reference_image).mask reference_image[mask] = 0 # Compute error values errors = [ util.image.relative_mse(m, reference_image, 0.01) # util.image.relative_mse_outlier_rejection(m, reference_image, 0.01) # util.image.smape(m, reference_image) for m in method_images ] error_metric_name = "relMSE" def tonemap(img): return figuregen.JPEG(util.image.lin_to_srgb(util.image.exposure(img, exposure)), quality=80) # Reference image ref_grid = figuregen.Grid(1, 1) ref_grid.get_element(0, 0).set_image(tonemap(reference_image)) ref_grid.get_element(0, 0).set_marker(cropA.get_marker_pos(), cropA.get_marker_size(), color=[255,255,255]) ref_grid.get_element(0, 0).set_marker(cropB.get_marker_pos(), cropB.get_marker_size(), color=[255,255,255]) ref_grid.set_col_titles("bottom", [scene_name]) def crop_error(img, crop): cropped_image = crop.crop(img) cropped_reference = crop.crop(reference_image) return util.image.relative_mse(cropped_image, cropped_reference, 0.01) # return util.image.smape(cropped_image, cropped_reference) crop_errors_A = [ crop_error(m, cropA) for m in method_images ] crop_errors_B = [ crop_error(m, cropB) for m in method_images ] def error_string(index, array, include_time=False): value = f"${array[index]:.2f}$ " if index == 0: speedup = "$(1.00\\times)$" elif index == 3: speedup = "\\textbf{" + f"$(\\mathbf{{ {array[index]/array[0]:.2f}\\times }})$" + "}" else: speedup = f"$({array[index]/array[0]:.2f}\\times)$" if times is not None and include_time: speedup += ", " + times[index] return value + speedup label_params = { "width_mm": 20, "height_mm": 4, "fontsize": 8, "offset_mm": [0, 0], "txt_padding_mm": 1, "bg_color": None, "txt_color": [255,255,255], "pos": "bottom_center" } def get_color(clr, i): if len(clr) == 2: return clr[i] else: return clr # Comparison grid crop_grid = figuregen.Grid(num_cols=len(methods) + 1, num_rows=2) for col in range(1, len(methods)+1): crop_grid[0, col].set_image(tonemap(cropA.crop(method_images[col-1]))) crop_grid[0, col].set_label(outline(error_string(col-1, crop_errors_A), get_color(outline_color, 0), get_color(text_color, 0)), **label_params) crop_grid[1, col].set_image(tonemap(cropB.crop(method_images[col-1]))) crop_grid[1, col].set_label(outline(error_string(col-1, crop_errors_B), get_color(outline_color, 1), get_color(text_color, 1)), **label_params) crop_grid[0, 0].set_image(tonemap(cropA.crop(reference_image))) crop_grid[0, 0].set_label(outline(f"{error_metric_name} (crop)", get_color(outline_color, 0), get_color(text_color, 0)), **label_params) crop_grid[1, 0].set_image(tonemap(cropB.crop(reference_image))) crop_grid[1, 0].set_label(outline(f"{error_metric_name} (crop)", get_color(outline_color, 1), get_color(text_color, 1)), **label_params) # Column titles names = ["Reference"] names.extend([ methods[i][0] for i in range(len(methods))]) error_strings = [ f"{error_metric_name}" + (", time" if times is not None else "") ] error_strings.extend([ error_string(i, errors, True) for i in range(len(methods)) ]) if show_method_names: crop_grid.set_col_titles("top", names) crop_grid.set_col_titles("bottom", error_strings) # Grid layout crop_grid.get_layout().set_padding(column=1, row=1) if show_method_names: crop_grid.get_layout().set_col_titles("top", fontsize=8, field_size_mm=2.8, offset_mm=0.25) crop_grid.get_layout().set_col_titles("bottom", fontsize=8, field_size_mm=2.8, offset_mm=0.5) # Reference layout ref_grid.get_layout().set_padding(right=1, bottom=0) if show_method_names: ref_grid.get_layout().set_col_titles("top", fontsize=8, field_size_mm=2.8, offset_mm=0.25) ref_grid.get_layout().set_col_titles("bottom", fontsize=8, field_size_mm=2.8, offset_mm=0.5) return [ref_grid, crop_grid]
[94, 163, 188], #light-blue [181, 63, 106], #pink [82, 110, 186], #blue [186, 98, 82] #orange-crab ] # generate test images img_blue = np.tile([x / 255 for x in colors[2]], (32, 64, 1)) img_yellow = np.tile([x / 255 for x in colors[0]], (32, 64, 1)) # load the two images images = [img_blue, img_yellow] # ------ Create Grid including markers, frames, (sub)titles, labels, etc. ------- n_rows, n_cols = 2, 3 grid = figuregen.Grid(n_rows, n_cols) # fill grid with image data for row in range(n_rows): for col in range(n_cols): img = figuregen.PNG(images[row]) grid.get_element(row, col).set_image(img) layout = grid.get_layout() layout.set_padding(top=0.5, bottom=1.5) # marker grid.get_element(0, 0).set_marker(pos=[32, 12], size=[15, 10], color=colors[4]) grid.get_element(0, 0).set_marker(pos=[1, 1], size=[15, 10], color=colors[-1],
def make_figure(frame: FrameData, title=True, add_placeholder=True): # Define the cropping area of the raw tech zoom-ins half_width = int(frame.method_images[0].shape[1] / 2) height = frame.method_images[0].shape[0] tech_crop_left = util.image.Cropbox(top=0, left=0, width=half_width, height=height, scale=1) tech_crop_right = util.image.Cropbox(top=0, left=half_width, width=half_width, height=height, scale=1) # Define the cropping areas (one half of the image each) third_width = int(frame.method_images[0].shape[1] / 3) left_crop = util.image.Cropbox(top=0, left=0, width=third_width, height=height, scale=1) center_crop = util.image.Cropbox(top=0, left=third_width, width=third_width, height=height, scale=1) right_crop = util.image.Cropbox(top=0, left=2 * third_width, width=third_width, height=height, scale=1) renderings = figuregen.Grid(1, 3) if title: renderings.set_title("top", "(c) MIS combinations") renderings.set_col_titles("top", [ f'{frame.methods[0][0]}', f'{frame.methods[1][0]}', f'{frame.methods[2][0]}', ]) renderings.set_col_titles("bottom", [ f"${frame.errors[0]:.3f}$", f"${frame.errors[1]:.3f}$", f"${frame.errors[2]:.3f}$", ]) left = renderings[0, 0] left.set_image(tonemap(left_crop.crop(frame.method_images[0]))) center = renderings[0, 1] center.set_image(tonemap(center_crop.crop(frame.method_images[1]))) right = renderings[0, 2] right.set_image(tonemap(right_crop.crop(frame.method_images[2]))) weights = figuregen.Grid(1, 3) if title: weights.set_col_titles("top", [ f"{frame.methods[0][0]}", f"{frame.methods[1][0]}", f"{frame.methods[2][0]}" ]) weights.set_title("top", "(d) MIS weights") # Compute the MIS weight images left = weights[0, 0] w, m = compute_weight(frame, 0, 0, 0) w = 1 - w m1 = 1 - m left.set_image(figuregen.JPEG(colormap(left_crop.crop(w)), quality=80)) center = weights[0, 1] w, m = compute_weight(frame, 1, 0, 0) w = 1 - w m2 = 1 - m center.set_image(figuregen.JPEG(colormap(center_crop.crop(w)), quality=80)) right = weights[0, 2] w, m = compute_weight(frame, 2, 0, 0) w = 1 - w m3 = 1 - m right.set_image(figuregen.JPEG(colormap(right_crop.crop(w)), quality=80)) weights.set_col_titles("bottom", [f"avg.: ${m1:.2f}$", f"${m2:.2f}$", f"${m3:.2f}$"]) # Add a simple color bar cbar = figuregen.Grid(1, 1) cbar[0, 0].set_image(figuregen.PNG(colorbar())) # Show crops of the raw technique results techs_grid = figuregen.Grid(1, 2) t2 = techs_grid[0, 0] t2.set_image( tonemap(tech_crop_left.crop(frame.raw_technique_images[1][0][0]))) t1 = techs_grid[0, 1] t1.set_image( tonemap(tech_crop_right.crop(frame.raw_technique_images[1][0][1]))) t2.set_frame(1, color=[0, 113, 188]) t1.set_frame(1, color=[175, 10, 38]) tech_error = [ util.image.relative_mse(frame.raw_technique_images[1][0][0], frame.raw_technique_images[0][0][0]), util.image.relative_mse(frame.raw_technique_images[1][0][1], frame.raw_technique_images[0][0][0]), ] techs_grid.set_col_titles("bottom", [ f"relMSE: ${tech_error[0]:.2f}$", f"${tech_error[1]:.2f} \\,({tech_error[1]/tech_error[0]:.2f}\\times)$", ]) if title: techs_grid.set_col_titles( "top", ["Merge at $\\mathbf{x}_1$", "Merge at $\\mathbf{x}_2$"]) techs_grid.set_title("top", "(b) Individual techniques") # placeholder for illustrations placeholder = figuregen.Grid(1, 1) placeholder[0, 0].set_image( figuregen.PNG( util.image.lin_to_srgb(np.tile(np.array([1, 1, 1]), (7, 2))))) if title: placeholder.set_title("top", "(a) Scene layout") # define the layout renderings.layout.set_col_titles("bottom", 2.8, offset_mm=0.5, fontsize=8) renderings.layout.set_padding(right=2.0, column=0.5) if title: renderings.layout.set_col_titles("top", 2.8, fontsize=8, offset_mm=0.0) renderings.layout.set_title("top", 2.8, fontsize=8, offset_mm=0.5) # align all other modules weights.copy_layout(renderings) cbar.copy_layout(renderings) techs_grid.copy_layout(renderings) placeholder.copy_layout(renderings) # Add extra paddings weights.layout.set_padding(right=0.5) cbar.layout.set_padding(right=4) if add_placeholder: return [placeholder, techs_grid, renderings, weights, cbar] else: return [techs_grid, renderings, weights, cbar]
def make_plot(): rs = [1.0 / 100.0, 1.0 / 10.0, 0.5, 1, 2, 10, 100] data = [] names = [] for config in scene_configs: method_images = [ simpleimageio.read( os.path.join(config["scene_folder"], folder, "render.exr")) for _, folder in methods ] reference_image = simpleimageio.read( os.path.join(config["scene_folder"], "reference.exr"))[:, :, :3] scene_err = [ util.image.relative_mse_outlier_rejection(m, reference_image, 0.01) for m in method_images ] speedups = [err / scene_err[-1] for err in scene_err] speedups.pop() data.append([rs, speedups]) names.append(config["scene_name"]) plot = figuregen.PgfLinePlot(aspect_ratio=0.6, data=data) plot.set_padding(3.5, 5) plot.set_axis_label('x', "Radius scale") plot.set_axis_label('y', "Error") colors = [ [232, 181, 88], [5, 142, 78], [94, 163, 188], [181, 63, 106], [126, 83, 172], [37, 85, 166], ] plot.set_colors(colors) plot.set_font(8, tex_package="{dfadobe}") plot.set_linewidth(1.5) plot.set_axis_properties('x', ticks=[1.0 / 100.0, 1.0 / 10.0, 0.5, 1, 2, 10]) plot.set_axis_properties('y', ticks=[0.1, 0.5, 1.0], use_log_scale=False, range=[0.1, 1.2]) plot.set_v_line(pos=1, color=[0, 0, 0], linestyle=(4, 6), linewidth_pt=0.6) plot_module = figuregen.Grid(1, 1) plot_module.get_element(0, 0).set_image(plot) reference_grid = figuregen.Grid(num_rows=2, num_cols=3) for i in range(6): e = reference_grid.get_element(int(i / 3), i % 3) img = simpleimageio.read( os.path.join(scene_configs[i]["scene_folder"], "reference.exr"))[:, :, :3] tm = tonemap(img, scene_configs[i]["exposure"]) e.set_image(tm) e.set_caption(scene_configs[i]["scene_name"]) e.set_frame(3, colors[i]) l = reference_grid.get_layout() l.set_caption(2.8, fontsize=8, offset_mm=0.5) l.set_padding(left=1) figuregen.figure([[plot_module, reference_grid]], 17.7, "Results/RadiusFigure.pdf", tex_packages=["{dfadobe}"])
def place_label(element, txt, pos='bottom_left'): element.set_label(txt, pos, width_mm=7.8, height_mm=2.5, offset_mm=[0.4, 0.4], fontsize=6, bg_color=[20, 20, 20], txt_color=[255, 255, 255], txt_padding_mm=0.2) # ---------- Horizontal Figure TOP ---------- top_cols = len(method_filenames) top_grid = figuregen.Grid(num_rows=1, num_cols=top_cols) # fill grid with image data for col in range(top_cols): e = top_grid.get_element(0, col) e.set_image(figuregen.PNG(get_image(method_filenames[col]))) if col == 0: # reference place_label(e, txt='relMSE') else: # Method rmse = get_error(method_filenames[col]) place_label(e, txt=rmse) # Add markers for all crops c_idx = 0 for c in crops: