From da9a553ad77c32becfb1fb187e44bc19ee04ec01 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Thu, 4 Jun 2020 09:00:03 +0800 Subject: [PATCH 01/19] [skip ci] tmp work save --- python/taichi/lang/expr.py | 3 +++ python/taichi/lang/matrix.py | 14 ++++++++++---- python/taichi/misc/gui.py | 27 +++++++++++++++++++-------- python/taichi/misc/image.py | 2 +- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/python/taichi/lang/expr.py b/python/taichi/lang/expr.py index 83332b710f3b7..d30083aa1e8eb 100644 --- a/python/taichi/lang/expr.py +++ b/python/taichi/lang/expr.py @@ -142,6 +142,9 @@ def shape(self): self.materialize_layout_callback() return self.snode().shape() + def shape_ext(self): + return () + def data_type(self): return self.snode().data_type() diff --git a/python/taichi/lang/matrix.py b/python/taichi/lang/matrix.py index 14f45b8448c8c..0f12aff946a03 100644 --- a/python/taichi/lang/matrix.py +++ b/python/taichi/lang/matrix.py @@ -497,11 +497,17 @@ def assign_renamed(x, y): from .meta import fill_matrix fill_matrix(self, val) + def shape_ext(self, as_vector=None): + if as_vector is None: + as_vector = self.m == 1 + shape_ext = (self.n, ) if as_vector else (self.n, self.m) + return shape_ext + def to_numpy(self, keep_dims=False): # Discussion: https://github.com/taichi-dev/taichi/pull/1046#issuecomment-633548858 as_vector = self.m == 1 and not keep_dims - dim_ext = (self.n, ) if as_vector else (self.n, self.m) - ret = np.empty(self.loop_range().shape() + dim_ext, + shape_ext = self.shape_ext(as_vector) + ret = np.empty(self.loop_range().shape() + shape_ext, dtype=to_numpy_type( self.loop_range().snode().data_type())) from .meta import matrix_to_ext_arr @@ -513,8 +519,8 @@ def to_numpy(self, keep_dims=False): def to_torch(self, device=None, keep_dims=False): import torch as_vector = self.m == 1 and not keep_dims - dim_ext = (self.n, ) if as_vector else (self.n, self.m) - ret = torch.empty(self.loop_range().shape() + dim_ext, + shape_ext = self.shape_ext(as_vector) + ret = torch.empty(self.loop_range().shape() + shape_ext, dtype=to_pytorch_type( self.loop_range().snode().data_type()), device=device) diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index 7267d8eb8d65a..071f361aa3ac3 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -49,30 +49,41 @@ def clear(self, color=None): def set_image(self, img): import numpy as np + import taichi as ti from .image import cook_image + from .util import get_os_name + + ti.profiler_start('cook_image') img = cook_image(img) + ti.profiler_stop() + + ti.profiler_start('astype') if img.dtype in [np.uint8, np.uint16, np.uint32, np.uint64]: img = img.astype(np.float32) * (1 / np.iinfo(img.dtype).max) elif img.dtype in [np.float32, np.float64]: - img = np.clip(img.astype(np.float32), 0, 1) + if img.dtype == np.float32 and get_os_name() == 'win': + img = np.clip(img.astype(np.float32), 0, 1) else: raise ValueError( f'Data type {img.dtype} not supported in GUI.set_image') + ti.profiler_stop() + + ti.profiler_start('concatenate') if len(img.shape) == 2: img = img[..., None] if img.shape[2] == 1: - img = img + np.zeros(shape=(1, 1, 4)) + img = img + np.zeros(shape=(1, 1, 4), dtype=np.float32) if img.shape[2] == 3: - img = np.concatenate([ - img, + img = np.concatenate([img, np.zeros(shape=(img.shape[0], img.shape[1], 1), - dtype=np.float32) - ], - axis=2) - img = img.astype(np.float32) + dtype=np.float32)], axis=2) assert img.shape[: 2] == self.res, "Image resolution does not match GUI resolution" + ti.profiler_stop() + + ti.profiler_start('set_img') self.core.set_img(np.ascontiguousarray(img).ctypes.data) + ti.profiler_stop() def circle(self, pos, color=0xFFFFFF, radius=1): self.canvas.circle_single(pos[0], pos[1], color, radius) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 30469709e4820..fcdbc72d2fb1f 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -4,7 +4,7 @@ def cook_image(img): if isinstance(img, ti.Matrix): - img = img.to_numpy(as_vector=True) + img = img.to_numpy() if isinstance(img, ti.Expr): img = img.to_numpy() assert isinstance(img, np.ndarray) From 7e57cfbcc0161a6d2af77532e5c1a453504314a4 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Thu, 4 Jun 2020 14:52:01 +0800 Subject: [PATCH 02/19] reduce some redulent numpy copy for f32 image --- examples/simple_uv.py | 29 +++++++++++++ python/taichi/lang/meta.py | 22 ++++++++++ python/taichi/misc/gui.py | 77 +++++++++++++++++++++------------ python/taichi/misc/image.py | 17 ++------ taichi/python/export_visual.cpp | 5 +++ 5 files changed, 110 insertions(+), 40 deletions(-) create mode 100644 examples/simple_uv.py diff --git a/examples/simple_uv.py b/examples/simple_uv.py new file mode 100644 index 0000000000000..8f77642db8fd4 --- /dev/null +++ b/examples/simple_uv.py @@ -0,0 +1,29 @@ +import taichi as ti +import numpy as np + +ti.init(arch=ti.cpu, print_ir=True) + +res = 1280, 720 +pixels = ti.Vector(3, dt=ti.f32, shape=res) + +@ti.kernel +def paint(size_x: ti.template(), size_y: ti.template()): + for i, j in pixels: + u = i / size_x + v = j / size_y + pixels[i, j] = [u, v, 0] + +gui = ti.GUI('UV', res) + +paint(res[0], res[1]) +gui.set_image(pixels) + +while not gui.get_event(ti.GUI.ESCAPE): + ti.profiler_start('paint') + paint(res[0], res[1]) + ti.sync() + ti.profiler_stop() + gui.set_image(pixels) + gui.show() + +ti.profiler_print() diff --git a/python/taichi/lang/meta.py b/python/taichi/lang/meta.py index 9fdc8e231461c..8707b21a71d8e 100644 --- a/python/taichi/lang/meta.py +++ b/python/taichi/lang/meta.py @@ -15,6 +15,28 @@ def tensor_to_ext_arr(tensor: ti.template(), arr: ti.ext_arr()): arr[I] = tensor[I] +@ti.func +def cook_image_type(x): + x = ti.cast(x, ti.f32) + # Issue 1005, don't know why win GUI doesn't do clamp for us + if ti.static(ti.get_os_name() == 'win'): + x = ti.min(1, ti.max(0, x)) + return x + + +@ti.kernel +def tensor_to_image(tensor: ti.template(), arr: ti.ext_arr()): + for I in ti.grouped(tensor): + arr[I] = cook_image_type(tensor[I]) + + +@ti.kernel +def vector_to_image(mat: ti.template(), arr: ti.ext_arr()): + for I in ti.grouped(mat): + for p in ti.static(range(mat.n)): + arr[I, p] = cook_image_type(mat[I][p]) + + @ti.kernel def tensor_to_tensor(tensor: ti.template(), other: ti.template()): for I in ti.grouped(tensor): diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index 071f361aa3ac3..c1ed3086b23b0 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -32,6 +32,7 @@ def __init__(self, name, res=512, background_color=0x0): if isinstance(res, numbers.Number): res = (res, res) self.res = res + self.img = np.ascontiguousarray(np.zeros(self.res + (4,), np.float32)) self.core = ti.core.GUI(name, ti.veci(*res)) self.canvas = self.core.get_canvas() self.background_color = background_color @@ -50,39 +51,61 @@ def clear(self, color=None): def set_image(self, img): import numpy as np import taichi as ti - from .image import cook_image - from .util import get_os_name - ti.profiler_start('cook_image') - img = cook_image(img) - ti.profiler_stop() + def cook_image(img): + if img.dtype in [np.uint8, np.uint16, np.uint32, np.uint64]: + img = img.astype(np.float32) * (1 / np.iinfo(img.dtype).max) + elif img.dtype in [np.float32, np.float64]: + img = img.astype(np.float32) + from .util import get_os_name + if img.dtype == np.float32 and get_os_name() == 'win': + img = np.clip(img, 0, 1) + else: + raise ValueError( + f'Data type {img.dtype} not supported in GUI.set_image') + + if len(img.shape) == 2: + img = img[..., None] + if img.shape[2] == 1: + img = img + np.zeros(shape=(1, 1, 4), dtype=np.float32) + if img.shape[2] == 3: + img = np.concatenate([img, + np.zeros(shape=(img.shape[0], img.shape[1], 1), + dtype=np.float32)], axis=2) + assert img.shape[:2] == self.res, "Image resolution does not match GUI resolution" + return np.ascontiguousarray(img) + + ti.profiler_start('copy_image') + if isinstance(img, ti.Expr): + if ti.core.is_integral(img.data_type()): + # image of uint is not optimized by xxx_to_image + self.img = cook_image(img.to_numpy()) + else: + assert img.shape() == self.res, "Image resolution does not match GUI resolution" + from taichi.lang.meta import tensor_to_image + tensor_to_image(img, self.img) + ti.sync() + + elif isinstance(img, ti.Matrix): + if ti.core.is_integral(img.data_type()): + self.img = cook_image(img.to_numpy()) + else: + assert img.shape() == self.res, "Image resolution does not match GUI resolution" + assert img.n in [3, 4], "Only greyscale, RGB or RGBA images are supported in GUI.set_image" + assert img.m == 1 + from taichi.lang.meta import vector_to_image + vector_to_image(img, self.img) + ti.sync() + + elif isinstance(img, np.ndarray): + self.img = cook_image(img) - ti.profiler_start('astype') - if img.dtype in [np.uint8, np.uint16, np.uint32, np.uint64]: - img = img.astype(np.float32) * (1 / np.iinfo(img.dtype).max) - elif img.dtype in [np.float32, np.float64]: - if img.dtype == np.float32 and get_os_name() == 'win': - img = np.clip(img.astype(np.float32), 0, 1) else: - raise ValueError( - f'Data type {img.dtype} not supported in GUI.set_image') - ti.profiler_stop() - - ti.profiler_start('concatenate') - if len(img.shape) == 2: - img = img[..., None] - if img.shape[2] == 1: - img = img + np.zeros(shape=(1, 1, 4), dtype=np.float32) - if img.shape[2] == 3: - img = np.concatenate([img, - np.zeros(shape=(img.shape[0], img.shape[1], 1), - dtype=np.float32)], axis=2) - assert img.shape[: - 2] == self.res, "Image resolution does not match GUI resolution" + raise ValueError(f"GUI.set_image only takes Taichi tensor or NumPy array, not {type(img)}") ti.profiler_stop() ti.profiler_start('set_img') - self.core.set_img(np.ascontiguousarray(img).ctypes.data) + self.core.set_img(self.img.ctypes.data) ti.profiler_stop() def circle(self, pos, color=0xFFFFFF, radius=1): diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index fcdbc72d2fb1f..881aa856e8fe2 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -1,19 +1,9 @@ import numpy as np import taichi as ti - -def cook_image(img): - if isinstance(img, ti.Matrix): - img = img.to_numpy() - if isinstance(img, ti.Expr): - img = img.to_numpy() - assert isinstance(img, np.ndarray) - assert len(img.shape) in [2, 3] - return img - - def imwrite(img, filename): - img = cook_image(img) + img = img.to_numpy() + assert len(img.shape) in [2, 3] resx, resy = img.shape[:2] if len(img.shape) == 3: comp = img.shape[2] @@ -35,7 +25,8 @@ def imread(filename, channels=0): def imshow(img, winname='Taichi'): - img = cook_image(img) + img = img.to_numpy() + assert len(img.shape) in [2, 3] gui = ti.GUI(winname, res=img.shape[:2]) while not gui.get_event(ti.GUI.ESCAPE): gui.set_image(img) diff --git a/taichi/python/export_visual.cpp b/taichi/python/export_visual.cpp index f28148b417129..6626cb0142792 100644 --- a/taichi/python/export_visual.cpp +++ b/taichi/python/export_visual.cpp @@ -22,6 +22,11 @@ void export_visual(py::module &m) { std::memcpy((void *)img.get_data().data(), (void *)ptr, img.get_data_size()); }) + .def("get_img_ptr", + [&](GUI *gui) -> std::size_t { + auto &img = gui->canvas->img; + return (size_t)img.get_data().data(); + }) .def("screenshot", &GUI::screenshot) .def("has_key_event", &GUI::has_key_event) .def("wait_key_event", &GUI::wait_key_event) From 3aaece3e1c3e3d20abbfa255c345568f81983feb Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Thu, 4 Jun 2020 15:03:28 +0800 Subject: [PATCH 03/19] [skip ci] no hoc --- examples/simple_uv.py | 11 +---------- misc/simple_uv.py | 31 +++++++++++++++++++++++++++++++ python/taichi/misc/gui.py | 4 ---- 3 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 misc/simple_uv.py diff --git a/examples/simple_uv.py b/examples/simple_uv.py index 8f77642db8fd4..7c68ae7adc8c1 100644 --- a/examples/simple_uv.py +++ b/examples/simple_uv.py @@ -1,7 +1,7 @@ import taichi as ti import numpy as np -ti.init(arch=ti.cpu, print_ir=True) +ti.init() res = 1280, 720 pixels = ti.Vector(3, dt=ti.f32, shape=res) @@ -14,16 +14,7 @@ def paint(size_x: ti.template(), size_y: ti.template()): pixels[i, j] = [u, v, 0] gui = ti.GUI('UV', res) - -paint(res[0], res[1]) -gui.set_image(pixels) - while not gui.get_event(ti.GUI.ESCAPE): - ti.profiler_start('paint') paint(res[0], res[1]) - ti.sync() - ti.profiler_stop() gui.set_image(pixels) gui.show() - -ti.profiler_print() diff --git a/misc/simple_uv.py b/misc/simple_uv.py new file mode 100644 index 0000000000000..92a4573e86535 --- /dev/null +++ b/misc/simple_uv.py @@ -0,0 +1,31 @@ +import taichi as ti +import numpy as np + +ti.init(arch=ti.cpu, print_ir=True) + +res = 1280, 720 +pixels = ti.Vector(3, dt=ti.f32, shape=res) + +@ti.kernel +def paint(size_x: ti.template(), size_y: ti.template()): + for i, j in pixels: + u = i / size_x + v = j / size_y + pixels[i, j] = [u, v, 0] + +gui = ti.GUI('UV', res) + +paint(res[0], res[1]) +gui.set_image(pixels) + +for i in range(200): + ti.profiler_start('paint') + paint(res[0], res[1]) + ti.sync() + ti.profiler_stop() + ti.profiler_start('set_image') + gui.set_image(pixels) + ti.profiler_stop() + gui.show() + +ti.profiler_print() diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index c1ed3086b23b0..9f6a5aae0d3fa 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -75,7 +75,6 @@ def cook_image(img): assert img.shape[:2] == self.res, "Image resolution does not match GUI resolution" return np.ascontiguousarray(img) - ti.profiler_start('copy_image') if isinstance(img, ti.Expr): if ti.core.is_integral(img.data_type()): # image of uint is not optimized by xxx_to_image @@ -102,11 +101,8 @@ def cook_image(img): else: raise ValueError(f"GUI.set_image only takes Taichi tensor or NumPy array, not {type(img)}") - ti.profiler_stop() - ti.profiler_start('set_img') self.core.set_img(self.img.ctypes.data) - ti.profiler_stop() def circle(self, pos, color=0xFFFFFF, radius=1): self.canvas.circle_single(pos[0], pos[1], color, radius) From 9a355b8982d01677576a9d0298b8e0453abb0fca Mon Sep 17 00:00:00 2001 From: Taichi Gardener Date: Thu, 4 Jun 2020 03:07:55 -0400 Subject: [PATCH 04/19] [skip ci] enforce code format --- examples/simple_uv.py | 10 ++++++---- misc/simple_uv.py | 10 ++++++---- python/taichi/misc/gui.py | 28 +++++++++++++++++++--------- python/taichi/misc/image.py | 1 + 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/examples/simple_uv.py b/examples/simple_uv.py index 7c68ae7adc8c1..262853b95fa34 100644 --- a/examples/simple_uv.py +++ b/examples/simple_uv.py @@ -6,12 +6,14 @@ res = 1280, 720 pixels = ti.Vector(3, dt=ti.f32, shape=res) + @ti.kernel def paint(size_x: ti.template(), size_y: ti.template()): - for i, j in pixels: - u = i / size_x - v = j / size_y - pixels[i, j] = [u, v, 0] + for i, j in pixels: + u = i / size_x + v = j / size_y + pixels[i, j] = [u, v, 0] + gui = ti.GUI('UV', res) while not gui.get_event(ti.GUI.ESCAPE): diff --git a/misc/simple_uv.py b/misc/simple_uv.py index 92a4573e86535..9403acdf93111 100644 --- a/misc/simple_uv.py +++ b/misc/simple_uv.py @@ -6,12 +6,14 @@ res = 1280, 720 pixels = ti.Vector(3, dt=ti.f32, shape=res) + @ti.kernel def paint(size_x: ti.template(), size_y: ti.template()): - for i, j in pixels: - u = i / size_x - v = j / size_y - pixels[i, j] = [u, v, 0] + for i, j in pixels: + u = i / size_x + v = j / size_y + pixels[i, j] = [u, v, 0] + gui = ti.GUI('UV', res) diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index 9f6a5aae0d3fa..a91c53448d0f0 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -32,7 +32,7 @@ def __init__(self, name, res=512, background_color=0x0): if isinstance(res, numbers.Number): res = (res, res) self.res = res - self.img = np.ascontiguousarray(np.zeros(self.res + (4,), np.float32)) + self.img = np.ascontiguousarray(np.zeros(self.res + (4, ), np.float32)) self.core = ti.core.GUI(name, ti.veci(*res)) self.canvas = self.core.get_canvas() self.background_color = background_color @@ -63,16 +63,20 @@ def cook_image(img): else: raise ValueError( f'Data type {img.dtype} not supported in GUI.set_image') - + if len(img.shape) == 2: img = img[..., None] if img.shape[2] == 1: img = img + np.zeros(shape=(1, 1, 4), dtype=np.float32) if img.shape[2] == 3: - img = np.concatenate([img, + img = np.concatenate([ + img, np.zeros(shape=(img.shape[0], img.shape[1], 1), - dtype=np.float32)], axis=2) - assert img.shape[:2] == self.res, "Image resolution does not match GUI resolution" + dtype=np.float32) + ], + axis=2) + assert img.shape[: + 2] == self.res, "Image resolution does not match GUI resolution" return np.ascontiguousarray(img) if isinstance(img, ti.Expr): @@ -80,7 +84,8 @@ def cook_image(img): # image of uint is not optimized by xxx_to_image self.img = cook_image(img.to_numpy()) else: - assert img.shape() == self.res, "Image resolution does not match GUI resolution" + assert img.shape( + ) == self.res, "Image resolution does not match GUI resolution" from taichi.lang.meta import tensor_to_image tensor_to_image(img, self.img) ti.sync() @@ -89,8 +94,11 @@ def cook_image(img): if ti.core.is_integral(img.data_type()): self.img = cook_image(img.to_numpy()) else: - assert img.shape() == self.res, "Image resolution does not match GUI resolution" - assert img.n in [3, 4], "Only greyscale, RGB or RGBA images are supported in GUI.set_image" + assert img.shape( + ) == self.res, "Image resolution does not match GUI resolution" + assert img.n in [ + 3, 4 + ], "Only greyscale, RGB or RGBA images are supported in GUI.set_image" assert img.m == 1 from taichi.lang.meta import vector_to_image vector_to_image(img, self.img) @@ -100,7 +108,9 @@ def cook_image(img): self.img = cook_image(img) else: - raise ValueError(f"GUI.set_image only takes Taichi tensor or NumPy array, not {type(img)}") + raise ValueError( + f"GUI.set_image only takes Taichi tensor or NumPy array, not {type(img)}" + ) self.core.set_img(self.img.ctypes.data) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 881aa856e8fe2..d250a7cce438f 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -1,6 +1,7 @@ import numpy as np import taichi as ti + def imwrite(img, filename): img = img.to_numpy() assert len(img.shape) in [2, 3] From 1780b6a86accc9bff8320bf64970979cc97fc1f6 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Fri, 5 Jun 2020 10:32:36 +0800 Subject: [PATCH 05/19] fix image test --- python/taichi/misc/image.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index d250a7cce438f..0e35fe7f56adc 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -3,7 +3,8 @@ def imwrite(img, filename): - img = img.to_numpy() + if not isinstance(np.ndarray): + img = img.to_numpy() assert len(img.shape) in [2, 3] resx, resy = img.shape[:2] if len(img.shape) == 3: @@ -26,7 +27,8 @@ def imread(filename, channels=0): def imshow(img, winname='Taichi'): - img = img.to_numpy() + if not isinstance(np.ndarray): + img = img.to_numpy() assert len(img.shape) in [2, 3] gui = ti.GUI(winname, res=img.shape[:2]) while not gui.get_event(ti.GUI.ESCAPE): From 721b1f15490af4964a9174586737e4959d05badc Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Fri, 5 Jun 2020 12:47:09 +0800 Subject: [PATCH 06/19] [skip ci] remove misc/simple_uv.py --- examples/simple_uv.py | 8 ++++---- misc/simple_uv.py | 33 --------------------------------- python/taichi/lang/meta.py | 2 +- 3 files changed, 5 insertions(+), 38 deletions(-) delete mode 100644 misc/simple_uv.py diff --git a/examples/simple_uv.py b/examples/simple_uv.py index 262853b95fa34..f9e30568c1ede 100644 --- a/examples/simple_uv.py +++ b/examples/simple_uv.py @@ -8,15 +8,15 @@ @ti.kernel -def paint(size_x: ti.template(), size_y: ti.template()): +def paint(): for i, j in pixels: - u = i / size_x - v = j / size_y + u = i / res[0] + v = j / res[1] pixels[i, j] = [u, v, 0] gui = ti.GUI('UV', res) while not gui.get_event(ti.GUI.ESCAPE): - paint(res[0], res[1]) + paint() gui.set_image(pixels) gui.show() diff --git a/misc/simple_uv.py b/misc/simple_uv.py deleted file mode 100644 index 9403acdf93111..0000000000000 --- a/misc/simple_uv.py +++ /dev/null @@ -1,33 +0,0 @@ -import taichi as ti -import numpy as np - -ti.init(arch=ti.cpu, print_ir=True) - -res = 1280, 720 -pixels = ti.Vector(3, dt=ti.f32, shape=res) - - -@ti.kernel -def paint(size_x: ti.template(), size_y: ti.template()): - for i, j in pixels: - u = i / size_x - v = j / size_y - pixels[i, j] = [u, v, 0] - - -gui = ti.GUI('UV', res) - -paint(res[0], res[1]) -gui.set_image(pixels) - -for i in range(200): - ti.profiler_start('paint') - paint(res[0], res[1]) - ti.sync() - ti.profiler_stop() - ti.profiler_start('set_image') - gui.set_image(pixels) - ti.profiler_stop() - gui.show() - -ti.profiler_print() diff --git a/python/taichi/lang/meta.py b/python/taichi/lang/meta.py index 8707b21a71d8e..74b411a23c380 100644 --- a/python/taichi/lang/meta.py +++ b/python/taichi/lang/meta.py @@ -18,7 +18,7 @@ def tensor_to_ext_arr(tensor: ti.template(), arr: ti.ext_arr()): @ti.func def cook_image_type(x): x = ti.cast(x, ti.f32) - # Issue 1005, don't know why win GUI doesn't do clamp for us + # Issue #1000, don't know why win GUI doesn't do clamp for us if ti.static(ti.get_os_name() == 'win'): x = ti.min(1, ti.max(0, x)) return x From c0953bdaf5d293d0ef6067658a4b8c3a590299fd Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Fri, 5 Jun 2020 12:58:12 +0800 Subject: [PATCH 07/19] [skip ci] remove get_img_ptr --- taichi/python/export_visual.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/taichi/python/export_visual.cpp b/taichi/python/export_visual.cpp index 6626cb0142792..f28148b417129 100644 --- a/taichi/python/export_visual.cpp +++ b/taichi/python/export_visual.cpp @@ -22,11 +22,6 @@ void export_visual(py::module &m) { std::memcpy((void *)img.get_data().data(), (void *)ptr, img.get_data_size()); }) - .def("get_img_ptr", - [&](GUI *gui) -> std::size_t { - auto &img = gui->canvas->img; - return (size_t)img.get_data().data(); - }) .def("screenshot", &GUI::screenshot) .def("has_key_event", &GUI::has_key_event) .def("wait_key_event", &GUI::wait_key_event) From 36b11e5e39ac69ea7823ae58ed154a65d798a598 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Fri, 5 Jun 2020 12:59:15 +0800 Subject: [PATCH 08/19] [skip ci] comment --- python/taichi/misc/gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index d16c2083f0a0c..b0ebbc3653d5c 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -32,6 +32,7 @@ def __init__(self, name, res=512, background_color=0x0): if isinstance(res, numbers.Number): res = (res, res) self.res = res + # The GUI canvas uses RGBA for storage, therefore we needs NxMx4 for a image self.img = np.ascontiguousarray(np.zeros(self.res + (4, ), np.float32)) self.core = ti.core.GUI(name, ti.veci(*res)) self.canvas = self.core.get_canvas() From 39564c8507511dc9fc706d94ef35300ac448a5d3 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Fri, 5 Jun 2020 13:11:45 +0800 Subject: [PATCH 09/19] fix image test again --- docs/gui.rst | 14 +++++++------- python/taichi/misc/image.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/gui.rst b/docs/gui.rst index 94a50322d20b2..436db4dce7116 100644 --- a/docs/gui.rst +++ b/docs/gui.rst @@ -89,7 +89,7 @@ Paint on a window .. note :: When using ``float32`` or ``float64`` as the data type, - ``img`` entries will be clipped into range ``[0, 1]``. + ``img`` entries will be clipped into range ``[0, 1]`` for display. .. function:: gui.circle(pos, color = 0xFFFFFF, radius = 1) @@ -169,14 +169,14 @@ Every event have a key and type. :: - ti.GUI.ESCAPE - ti.GUI.SHIFT - ti.GUI.LEFT - 'a' + ti.GUI.ESCAPE # Esc + ti.GUI.SHIFT # Shift + ti.GUI.LEFT # Left Arrow + 'a' # we use lowercase for alphabet 'b' ... - ti.GUI.LMB - ti.GUI.RMB + ti.GUI.LMB # Left Mouse Button + ti.GUI.RMB # Right Mouse Button *Event type* is the type of event, for now, there are just three type of event: diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 0e35fe7f56adc..3ae20e210ec91 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -3,7 +3,7 @@ def imwrite(img, filename): - if not isinstance(np.ndarray): + if not isinstance(img, np.ndarray): img = img.to_numpy() assert len(img.shape) in [2, 3] resx, resy = img.shape[:2] @@ -27,7 +27,7 @@ def imread(filename, channels=0): def imshow(img, winname='Taichi'): - if not isinstance(np.ndarray): + if not isinstance(img, np.ndarray): img = img.to_numpy() assert len(img.shape) in [2, 3] gui = ti.GUI(winname, res=img.shape[:2]) From 8b6e8fe462bec202b9e0dc0d428e65b6ab39028c Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Fri, 5 Jun 2020 22:26:39 +0800 Subject: [PATCH 10/19] [skip ci] no shape_ext --- python/taichi/lang/expr.py | 3 --- python/taichi/lang/matrix.py | 10 ++-------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/python/taichi/lang/expr.py b/python/taichi/lang/expr.py index 9f56ac3518e44..3009607755244 100644 --- a/python/taichi/lang/expr.py +++ b/python/taichi/lang/expr.py @@ -146,9 +146,6 @@ def shape(self): self.materialize_layout_callback() return self.snode().shape() - def shape_ext(self): - return () - def data_type(self): return self.snode().data_type() diff --git a/python/taichi/lang/matrix.py b/python/taichi/lang/matrix.py index 75ccca7f02c9f..1feb7a8a907f3 100644 --- a/python/taichi/lang/matrix.py +++ b/python/taichi/lang/matrix.py @@ -604,12 +604,6 @@ def assign_renamed(x, y): from .meta import fill_matrix fill_matrix(self, val) - def shape_ext(self, as_vector=None): - if as_vector is None: - as_vector = self.m == 1 - shape_ext = (self.n, ) if as_vector else (self.n, self.m) - return shape_ext - @python_scope def to_numpy(self, keep_dims=False, as_vector=None): # Discussion: https://github.com/taichi-dev/taichi/pull/1046#issuecomment-633548858 @@ -621,7 +615,7 @@ def to_numpy(self, keep_dims=False, as_vector=None): DeprecationWarning, stacklevel=3) as_vector = self.m == 1 and not keep_dims - shape_ext = self.shape_ext(as_vector) + shape_ext = (self.n, ) if as_vector else (self.n, self.m) ret = np.empty(self.loop_range().shape() + shape_ext, dtype=to_numpy_type( self.loop_range().snode().data_type())) @@ -635,7 +629,7 @@ def to_numpy(self, keep_dims=False, as_vector=None): def to_torch(self, device=None, keep_dims=False): import torch as_vector = self.m == 1 and not keep_dims - shape_ext = self.shape_ext(as_vector) + shape_ext = (self.n, ) if as_vector else (self.n, self.m) ret = torch.empty(self.loop_range().shape() + shape_ext, dtype=to_pytorch_type( self.loop_range().snode().data_type()), From 2f9b38df076934457a10243ed895aa9a02b750ab Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Sat, 6 Jun 2020 12:37:16 +0800 Subject: [PATCH 11/19] [skip ci] From 77b6aa0dc881ad6f8e77333b33736060432db976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E4=BA=8E=E6=96=8C?= <1931127624@qq.com> Date: Sat, 6 Jun 2020 12:42:10 +0800 Subject: [PATCH 12/19] [skip ci] Apply suggestions from code review Co-authored-by: Yuanming Hu --- python/taichi/lang/meta.py | 4 ++-- python/taichi/misc/gui.py | 4 ++-- python/taichi/misc/image.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/taichi/lang/meta.py b/python/taichi/lang/meta.py index 74b411a23c380..ed2350ff75ec3 100644 --- a/python/taichi/lang/meta.py +++ b/python/taichi/lang/meta.py @@ -18,9 +18,9 @@ def tensor_to_ext_arr(tensor: ti.template(), arr: ti.ext_arr()): @ti.func def cook_image_type(x): x = ti.cast(x, ti.f32) - # Issue #1000, don't know why win GUI doesn't do clamp for us + # Issue #1000, we don't know why Windows GUI does not do clamping for us if ti.static(ti.get_os_name() == 'win'): - x = ti.min(1, ti.max(0, x)) + x = min(1, max(0, x)) return x diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index b0ebbc3653d5c..417ea7c297bee 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -32,7 +32,7 @@ def __init__(self, name, res=512, background_color=0x0): if isinstance(res, numbers.Number): res = (res, res) self.res = res - # The GUI canvas uses RGBA for storage, therefore we needs NxMx4 for a image + # The GUI canvas uses RGBA for storage, therefore we need NxMx4 for an image. self.img = np.ascontiguousarray(np.zeros(self.res + (4, ), np.float32)) self.core = ti.core.GUI(name, ti.veci(*res)) self.canvas = self.core.get_canvas() @@ -110,7 +110,7 @@ def cook_image(img): else: raise ValueError( - f"GUI.set_image only takes Taichi tensor or NumPy array, not {type(img)}" + f"GUI.set_image only takes a Taichi tensor or NumPy array, not {type(img)}" ) self.core.set_img(self.img.ctypes.data) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 3ae20e210ec91..88e3b7b049750 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -26,11 +26,11 @@ def imread(filename, channels=0): return img.swapaxes(0, 1)[:, ::-1, :] -def imshow(img, winname='Taichi'): +def imshow(img, window_name='Taichi'): if not isinstance(img, np.ndarray): img = img.to_numpy() assert len(img.shape) in [2, 3] - gui = ti.GUI(winname, res=img.shape[:2]) + gui = ti.GUI(window_name, res=img.shape[:2]) while not gui.get_event(ti.GUI.ESCAPE): gui.set_image(img) gui.show() From e7f59ce9beb0b21ffeadfa20c5a936b590a30798 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Sat, 6 Jun 2020 12:43:34 +0800 Subject: [PATCH 13/19] [skip travis] self.cook_image --- python/taichi/misc/gui.py | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index 417ea7c297bee..bd6fc4755198a 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -49,41 +49,41 @@ def clear(self, color=None): color = self.background_color self.canvas.clear(color) + def cook_image(self, img): + if img.dtype in [np.uint8, np.uint16, np.uint32, np.uint64]: + img = img.astype(np.float32) * (1 / np.iinfo(img.dtype).max) + elif img.dtype in [np.float32, np.float64]: + img = img.astype(np.float32) + from .util import get_os_name + if img.dtype == np.float32 and get_os_name() == 'win': + img = np.clip(img, 0, 1) + else: + raise ValueError( + f'Data type {img.dtype} not supported in GUI.set_image') + + if len(img.shape) == 2: + img = img[..., None] + if img.shape[2] == 1: + img = img + np.zeros(shape=(1, 1, 4), dtype=np.float32) + if img.shape[2] == 3: + img = np.concatenate([ + img, + np.zeros(shape=(img.shape[0], img.shape[1], 1), + dtype=np.float32) + ], + axis=2) + assert img.shape[: + 2] == self.res, "Image resolution does not match GUI resolution" + return np.ascontiguousarray(img) + def set_image(self, img): import numpy as np import taichi as ti - def cook_image(img): - if img.dtype in [np.uint8, np.uint16, np.uint32, np.uint64]: - img = img.astype(np.float32) * (1 / np.iinfo(img.dtype).max) - elif img.dtype in [np.float32, np.float64]: - img = img.astype(np.float32) - from .util import get_os_name - if img.dtype == np.float32 and get_os_name() == 'win': - img = np.clip(img, 0, 1) - else: - raise ValueError( - f'Data type {img.dtype} not supported in GUI.set_image') - - if len(img.shape) == 2: - img = img[..., None] - if img.shape[2] == 1: - img = img + np.zeros(shape=(1, 1, 4), dtype=np.float32) - if img.shape[2] == 3: - img = np.concatenate([ - img, - np.zeros(shape=(img.shape[0], img.shape[1], 1), - dtype=np.float32) - ], - axis=2) - assert img.shape[: - 2] == self.res, "Image resolution does not match GUI resolution" - return np.ascontiguousarray(img) - if isinstance(img, ti.Expr): if ti.core.is_integral(img.data_type()): # image of uint is not optimized by xxx_to_image - self.img = cook_image(img.to_numpy()) + self.img = self.cook_image(img.to_numpy()) else: assert img.shape( ) == self.res, "Image resolution does not match GUI resolution" @@ -93,7 +93,7 @@ def cook_image(img): elif isinstance(img, ti.Matrix): if ti.core.is_integral(img.data_type()): - self.img = cook_image(img.to_numpy()) + self.img = self.cook_image(img.to_numpy()) else: assert img.shape( ) == self.res, "Image resolution does not match GUI resolution" @@ -106,7 +106,7 @@ def cook_image(img): ti.sync() elif isinstance(img, np.ndarray): - self.img = cook_image(img) + self.img = self.cook_image(img) else: raise ValueError( From 686d8081be452c1e415b0a257811fa265264cea1 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Sat, 6 Jun 2020 12:49:05 +0800 Subject: [PATCH 14/19] [skip travis] fix torch test --- python/taichi/lang/matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/taichi/lang/matrix.py b/python/taichi/lang/matrix.py index bf34fb8cb7423..45c212b6e70c0 100644 --- a/python/taichi/lang/matrix.py +++ b/python/taichi/lang/matrix.py @@ -3,7 +3,7 @@ import copy import numbers import numpy as np -from .util import taichi_scope, python_scope, deprecated, to_numpy_type +from .util import taichi_scope, python_scope, deprecated, to_numpy_type, to_pytorch_type from .common_ops import TaichiOperations from collections.abc import Iterable From 0d46229be5cdaf069067cc079a08fd41dea69c2a Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Sat, 6 Jun 2020 12:50:36 +0800 Subject: [PATCH 15/19] [skip ci] better message --- python/taichi/misc/image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 88e3b7b049750..73a956981dde2 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -5,7 +5,7 @@ def imwrite(img, filename): if not isinstance(img, np.ndarray): img = img.to_numpy() - assert len(img.shape) in [2, 3] + assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" resx, resy = img.shape[:2] if len(img.shape) == 3: comp = img.shape[2] @@ -29,7 +29,7 @@ def imread(filename, channels=0): def imshow(img, window_name='Taichi'): if not isinstance(img, np.ndarray): img = img.to_numpy() - assert len(img.shape) in [2, 3] + assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" gui = ti.GUI(window_name, res=img.shape[:2]) while not gui.get_event(ti.GUI.ESCAPE): gui.set_image(img) From 3d34e110992e6a4b8c5ed6b08bea30404d356132 Mon Sep 17 00:00:00 2001 From: Taichi Gardener Date: Sat, 6 Jun 2020 00:51:03 -0400 Subject: [PATCH 16/19] [skip ci] enforce code format --- python/taichi/misc/image.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 73a956981dde2..9f2db5cbc4ebd 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -5,7 +5,8 @@ def imwrite(img, filename): if not isinstance(img, np.ndarray): img = img.to_numpy() - assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" + assert len(img.shape) in [2, + 3], "Image must be either RGB/RGBA or greyscale" resx, resy = img.shape[:2] if len(img.shape) == 3: comp = img.shape[2] @@ -29,7 +30,8 @@ def imread(filename, channels=0): def imshow(img, window_name='Taichi'): if not isinstance(img, np.ndarray): img = img.to_numpy() - assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" + assert len(img.shape) in [2, + 3], "Image must be either RGB/RGBA or greyscale" gui = ti.GUI(window_name, res=img.shape[:2]) while not gui.get_event(ti.GUI.ESCAPE): gui.set_image(img) From cd3e1b43b5a3aa2342e29abda75c5dd392d6ba89 Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Sat, 6 Jun 2020 16:32:18 +0800 Subject: [PATCH 17/19] fix win32 not clamping --- examples/image_io.py | 24 ------------------------ python/taichi/lang/meta.py | 8 ++++---- python/taichi/misc/gui.py | 2 -- taichi/gui/win32.cpp | 7 ++++--- taichi/gui/x11.cpp | 2 +- 5 files changed, 9 insertions(+), 34 deletions(-) delete mode 100644 examples/image_io.py diff --git a/examples/image_io.py b/examples/image_io.py deleted file mode 100644 index 11b8aa440f01d..0000000000000 --- a/examples/image_io.py +++ /dev/null @@ -1,24 +0,0 @@ -import taichi as ti -import os - -pixel = ti.var(ti.u8, shape=(512, 512, 3)) - - -@ti.kernel -def paint(): - for I in ti.grouped(pixel): - pixel[I] = ti.random() * 255 - - -paint() -pixel = pixel.to_numpy() -ti.imshow(pixel, 'Random Generated') -for ext in ['bmp', 'png', 'jpg']: - fn = 'taichi-example-random-img.' + ext - ti.imwrite(pixel, fn) - pixel_r = ti.imread(fn) - if ext != 'jpg': - assert (pixel_r == pixel).all() - else: - ti.imshow(pixel_r, 'JPEG Read Result') - os.remove(fn) diff --git a/python/taichi/lang/meta.py b/python/taichi/lang/meta.py index ed2350ff75ec3..ae38f5f7b5a5f 100644 --- a/python/taichi/lang/meta.py +++ b/python/taichi/lang/meta.py @@ -18,16 +18,16 @@ def tensor_to_ext_arr(tensor: ti.template(), arr: ti.ext_arr()): @ti.func def cook_image_type(x): x = ti.cast(x, ti.f32) - # Issue #1000, we don't know why Windows GUI does not do clamping for us - if ti.static(ti.get_os_name() == 'win'): - x = min(1, max(0, x)) return x @ti.kernel def tensor_to_image(tensor: ti.template(), arr: ti.ext_arr()): for I in ti.grouped(tensor): - arr[I] = cook_image_type(tensor[I]) + t = cook_image_type(tensor[I]) + arr[I, 0] = t + arr[I, 1] = t + arr[I, 2] = t @ti.kernel diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index bd6fc4755198a..aee8603341fab 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -55,8 +55,6 @@ def cook_image(self, img): elif img.dtype in [np.float32, np.float64]: img = img.astype(np.float32) from .util import get_os_name - if img.dtype == np.float32 and get_os_name() == 'win': - img = np.clip(img, 0, 1) else: raise ValueError( f'Data type {img.dtype} not supported in GUI.set_image') diff --git a/taichi/gui/win32.cpp b/taichi/gui/win32.cpp index eb3e045c1de0b..0c24ead17c5fc 100644 --- a/taichi/gui/win32.cpp +++ b/taichi/gui/win32.cpp @@ -180,9 +180,10 @@ void GUI::redraw() { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { auto c = reinterpret_cast(data + (j * width) + i); - c[0] = (unsigned char)(canvas->img[i][height - j - 1][2] * 255.0_f); - c[1] = (unsigned char)(canvas->img[i][height - j - 1][1] * 255.0_f); - c[2] = (unsigned char)(canvas->img[i][height - j - 1][0] * 255.0_f); + auto d = canvas->img[i][height - j - 1]; + c[0] = uint8(clamp(int(d[2] * 255.0_f), 0, 255)); + c[1] = uint8(clamp(int(d[1] * 255.0_f), 0, 255)); + c[2] = uint8(clamp(int(d[0] * 255.0_f), 0, 255)); c[3] = 0; } } diff --git a/taichi/gui/x11.cpp b/taichi/gui/x11.cpp index 84aaf919ef174..b8f2e0a02dc69 100644 --- a/taichi/gui/x11.cpp +++ b/taichi/gui/x11.cpp @@ -35,7 +35,7 @@ class CXImage { *p++ = uint8(clamp(int(c[2] * 255.0_f), 0, 255)); *p++ = uint8(clamp(int(c[1] * 255.0_f), 0, 255)); *p++ = uint8(clamp(int(c[0] * 255.0_f), 0, 255)); - *p++ = uint8(clamp(int(c[3] * 255.0_f), 0, 255)); + *p++ = 0; } } } From 79e4883017bf51d3a08d10206911fe7a7e8df88c Mon Sep 17 00:00:00 2001 From: archibate <17721388340@163.com> Date: Sat, 6 Jun 2020 17:37:24 +0800 Subject: [PATCH 18/19] [skip ci] fix imread/write --- python/taichi/misc/gui.py | 17 +++++++---------- python/taichi/misc/image.py | 22 +++++++++++++++++----- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/python/taichi/misc/gui.py b/python/taichi/misc/gui.py index aee8603341fab..0205ccd651a26 100644 --- a/python/taichi/misc/gui.py +++ b/python/taichi/misc/gui.py @@ -54,24 +54,21 @@ def cook_image(self, img): img = img.astype(np.float32) * (1 / np.iinfo(img.dtype).max) elif img.dtype in [np.float32, np.float64]: img = img.astype(np.float32) - from .util import get_os_name else: raise ValueError( f'Data type {img.dtype} not supported in GUI.set_image') if len(img.shape) == 2: img = img[..., None] + if img.shape[2] == 1: - img = img + np.zeros(shape=(1, 1, 4), dtype=np.float32) + img = img + np.zeros((1, 1, 4), np.float32) if img.shape[2] == 3: - img = np.concatenate([ - img, - np.zeros(shape=(img.shape[0], img.shape[1], 1), - dtype=np.float32) - ], - axis=2) - assert img.shape[: - 2] == self.res, "Image resolution does not match GUI resolution" + zeros = np.zeros((img.shape[0], img.shape[1], 1), np.float32) + img = np.concatenate([img, zeros], axis=2) + + res = img.shape[:2] + assert res == self.res, "Image resolution does not match GUI resolution" return np.ascontiguousarray(img) def set_image(self, img): diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 9f2db5cbc4ebd..c1370b7aea9e8 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -5,13 +5,24 @@ def imwrite(img, filename): if not isinstance(img, np.ndarray): img = img.to_numpy() - assert len(img.shape) in [2, - 3], "Image must be either RGB/RGBA or greyscale" + + if img.dtype in [np.uint16, np.uint32, np.uint64]: + img = (img // (np.iinfo(img.dtype).max / 256)).astype(np.uint8) + elif img.dtype in [np.float32, np.float64]: + img = (np.clip(img, 0, 1) * 255.0 + 0.5).astype(np.uint8) + elif img.dtype != np.uint8: + raise ValueError( + f'Data type {img.dtype} not supported in ti.imwrite') + + assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" + assert img.shape[2] in [1, 3, 4], "Image must be either RGB/RGBA or greyscale" + resx, resy = img.shape[:2] - if len(img.shape) == 3: - comp = img.shape[2] - else: + if len(img.shape) == 2: comp = 1 + else: + comp = img.shape[2] + img = np.ascontiguousarray(img.swapaxes(0, 1)[::-1, :, :]) ptr = img.ctypes.data ti.core.imwrite(filename, ptr, resx, resy, comp) @@ -33,6 +44,7 @@ def imshow(img, window_name='Taichi'): assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" gui = ti.GUI(window_name, res=img.shape[:2]) + img = gui.cook_image(img) while not gui.get_event(ti.GUI.ESCAPE): gui.set_image(img) gui.show() From 5d9a5ae127d342c629477ef2db65d9b20d816df9 Mon Sep 17 00:00:00 2001 From: Taichi Gardener Date: Sat, 6 Jun 2020 05:38:15 -0400 Subject: [PATCH 19/19] [skip ci] enforce code format --- python/taichi/misc/image.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index c1370b7aea9e8..09cbb1fd98973 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -11,11 +11,12 @@ def imwrite(img, filename): elif img.dtype in [np.float32, np.float64]: img = (np.clip(img, 0, 1) * 255.0 + 0.5).astype(np.uint8) elif img.dtype != np.uint8: - raise ValueError( - f'Data type {img.dtype} not supported in ti.imwrite') + raise ValueError(f'Data type {img.dtype} not supported in ti.imwrite') - assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" - assert img.shape[2] in [1, 3, 4], "Image must be either RGB/RGBA or greyscale" + assert len(img.shape) in [2, + 3], "Image must be either RGB/RGBA or greyscale" + assert img.shape[2] in [1, 3, + 4], "Image must be either RGB/RGBA or greyscale" resx, resy = img.shape[:2] if len(img.shape) == 2: