From a705b3678ffbb7f0d9b23d6805fb4362b50f8f73 Mon Sep 17 00:00:00 2001 From: archibate <1931127624@qq.com> Date: Tue, 23 Jun 2020 00:07:36 +0800 Subject: [PATCH 1/5] [skip ci] add ti.imdisplay for IPython users --- python/taichi/__init__.py | 2 +- python/taichi/misc/image.py | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/python/taichi/__init__.py b/python/taichi/__init__.py index 6c9a82c538c11..8cd8c17b36f63 100644 --- a/python/taichi/__init__.py +++ b/python/taichi/__init__.py @@ -7,7 +7,7 @@ from taichi.misc import * from taichi.misc.gui import GUI from taichi.misc.np2ply import PLYWriter -from taichi.misc.image import imread, imwrite, imshow +from taichi.misc.image import * from taichi.misc.task import Task from taichi.misc.test import * from taichi.misc import settings as settings diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index b3ec797cb557b..885fdb4d700d5 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -2,7 +2,7 @@ import taichi as ti -def imwrite(img, filename): +def imcook(img): if not isinstance(img, np.ndarray): img = img.to_numpy() @@ -23,7 +23,30 @@ def imwrite(img, filename): comp = img.shape[2] assert comp in [1, 3, 4], "Image must be either RGB/RGBA or greyscale" - img = np.ascontiguousarray(img.swapaxes(0, 1)[::-1, :]) + return img.swapaxes(0, 1)[::-1, :] + + + +def imdisplay(img): + """ + Try to display image in interactive shell. + """ + if ti.lang.shell.oinspect.name.startswith('IPython'): + import PIL.Image + from io import BytesIO + import IPython.display + import numpy as np + img = imcook(img) + f = BytesIO() + PIL.Image.fromarray(img).save(f, 'png') + IPython.display.display(IPython.display.Image(data=f.getvalue())) + else: + ti.imshow(img) + + +def imwrite(img, filename): + img = imcook(img) + img = np.ascontiguousarray(img) ptr = img.ctypes.data ti.core.imwrite(filename, ptr, resx, resy, comp) From ba46bd4aa7a34cb5d59e690ba956ed9a9a29225b Mon Sep 17 00:00:00 2001 From: archibate <1931127624@qq.com> Date: Wed, 24 Jun 2020 15:03:06 +0800 Subject: [PATCH 2/5] merge master --- python/taichi/lang/kernel.py | 4 +++- taichi/backends/opengl/opengl_api.cpp | 3 ++- taichi/python/export_misc.cpp | 6 ++++++ taichi/python/print_buffer.h | 25 +++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 taichi/python/print_buffer.h diff --git a/python/taichi/lang/kernel.py b/python/taichi/lang/kernel.py index 2b14f6b714c93..16e7b2057752a 100644 --- a/python/taichi/lang/kernel.py +++ b/python/taichi/lang/kernel.py @@ -456,7 +456,9 @@ def __call__(self, *args, **kwargs): instance_id, arg_features = self.mapper.lookup(args) key = (self.func, instance_id) self.materialize(key=key, args=args, arg_features=arg_features) - return self.compiled_functions[key](*args) + ret = self.compiled_functions[key](*args) + print(taichi_lang_core.pop_python_print_buffer(), end='') + return ret # For a Taichi class definition like below: diff --git a/taichi/backends/opengl/opengl_api.cpp b/taichi/backends/opengl/opengl_api.cpp index adfd1b663cd15..8efc776d0658d 100644 --- a/taichi/backends/opengl/opengl_api.cpp +++ b/taichi/backends/opengl/opengl_api.cpp @@ -5,6 +5,7 @@ #include "taichi/program/kernel.h" #include "taichi/program/program.h" #include "taichi/util/environ_config.h" +#include "taichi/python/print_buffer.h" #ifdef TI_WITH_OPENGL #include "glad/glad.h" @@ -482,7 +483,7 @@ struct CompiledProgram::Impl { TI_WARN("[glsl] Unexpected serialization type: {}, ignoring", type); break; }; - std::cout << str; + py_cout << str; } } rt_buf->msg_count = 0; diff --git a/taichi/python/export_misc.cpp b/taichi/python/export_misc.cpp index 2c3c250692b6b..e48a3b8fb9d7c 100644 --- a/taichi/python/export_misc.cpp +++ b/taichi/python/export_misc.cpp @@ -7,6 +7,7 @@ #include "taichi/common/task.h" #include "taichi/math/math.h" #include "taichi/python/exception.h" +#include "taichi/python/print_buffer.h" #include "taichi/python/export.h" #include "taichi/system/benchmark.h" #include "taichi/system/profiler.h" @@ -20,6 +21,8 @@ TI_NAMESPACE_BEGIN +PythonPrintBuffer py_cout; + Config config_from_py_dict(py::dict &c) { Config config; for (auto item : c) { @@ -153,6 +156,9 @@ void export_misc(py::module &m) { } printf("test was successful.\n"); }); + m.def("pop_python_print_buffer", []() { + return py_cout.pop_content(); + }); m.def("with_cuda", is_cuda_api_available); m.def("with_metal", taichi::lang::metal::is_metal_api_available); m.def("with_opengl", taichi::lang::opengl::is_opengl_api_available); diff --git a/taichi/python/print_buffer.h b/taichi/python/print_buffer.h new file mode 100644 index 0000000000000..857990cc04e50 --- /dev/null +++ b/taichi/python/print_buffer.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +TI_NAMESPACE_BEGIN + +struct PythonPrintBuffer { + /* holds kernel print result before switching back to python */ + std::stringstream ss; + + template + PythonPrintBuffer &operator<<(const T &t) { + ss << t; + return *this; + } + std::string pop_content() { + auto ret = ss.str(); + ss = std::stringstream(); + return ret; + } +}; + +extern PythonPrintBuffer py_cout; + +TI_NAMESPACE_END From ed0a07221904dcb9002b2c858435b2dea1e58af9 Mon Sep 17 00:00:00 2001 From: archibate <1931127624@qq.com> Date: Thu, 25 Jun 2020 00:58:34 +0800 Subject: [PATCH 3/5] not print until ti.sync(); fix imcook bug --- docs/syntax.rst | 16 +++++++++------- python/taichi/lang/impl.py | 3 +++ python/taichi/lang/kernel.py | 4 +--- python/taichi/misc/image.py | 9 ++++----- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/syntax.rst b/docs/syntax.rst index 24d2c32411e56..22b7a39191aee 100644 --- a/docs/syntax.rst +++ b/docs/syntax.rst @@ -226,16 +226,14 @@ Debug your program with ``print()`` in Taichi-scope. For example: print('v is', v) #=> v is [3, 4] -.. note:: - - For now, print is only supported on CPU, CUDA and OpenGL backends. +.. warning:: - For the CUDA backend, the printed result won't shows up until ``ti.sync()``: + General speaking, the printed result won't shows up until ``ti.sync()``: .. code-block:: python import taichi as ti - ti.init(arch=ti.cuda) + ti.init(arch=ti.gpu) @ti.kernel def kern(): @@ -254,6 +252,10 @@ Debug your program with ``print()`` in Taichi-scope. For example: before kernel after kernel inside kernel - after + after sync + + This is due to the fact that GPU memories are only copied on demand, i.e. + only when the output data is accessed, or performance will be harmed. - Also note that host access or program end will also implicitly invoke for ``ti.sync()``. + Also note that host access or program end will also implicitly invoke for + ``ti.sync()``. diff --git a/python/taichi/lang/impl.py b/python/taichi/lang/impl.py index fa5212ae2a3f4..a8884e9bdbff1 100644 --- a/python/taichi/lang/impl.py +++ b/python/taichi/lang/impl.py @@ -198,6 +198,9 @@ def get_tape(self, loss=None): def sync(self): self.materialize() self.prog.synchronize() + # print's in kernel won't take effect until ti.sync(), discussion: + # https://github.com/taichi-dev/taichi/pull/1303#discussion_r444897102 + print(taichi_lang_core.pop_python_print_buffer(), end='') pytaichi = PyTaichi() diff --git a/python/taichi/lang/kernel.py b/python/taichi/lang/kernel.py index 16e7b2057752a..2b14f6b714c93 100644 --- a/python/taichi/lang/kernel.py +++ b/python/taichi/lang/kernel.py @@ -456,9 +456,7 @@ def __call__(self, *args, **kwargs): instance_id, arg_features = self.mapper.lookup(args) key = (self.func, instance_id) self.materialize(key=key, args=args, arg_features=arg_features) - ret = self.compiled_functions[key](*args) - print(taichi_lang_core.pop_python_print_buffer(), end='') - return ret + return self.compiled_functions[key](*args) # For a Taichi class definition like below: diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 885fdb4d700d5..870e5f105cfe2 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -16,12 +16,10 @@ def imcook(img): assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" - resx, resy = img.shape[:2] if len(img.shape) == 2: - comp = 1 - else: - comp = img.shape[2] - assert comp in [1, 3, 4], "Image must be either RGB/RGBA or greyscale" + img = img.reshape(*img.shape, 1) + + assert img.shape[2] in [1, 3, 4], "Image must be either RGB/RGBA or greyscale" return img.swapaxes(0, 1)[::-1, :] @@ -48,6 +46,7 @@ def imwrite(img, filename): img = imcook(img) img = np.ascontiguousarray(img) ptr = img.ctypes.data + resy, resx, comp = img.shape ti.core.imwrite(filename, ptr, resx, resy, comp) From 15d5d0e79245db157b4c419276619450ff79d44d Mon Sep 17 00:00:00 2001 From: archibate <1931127624@qq.com> Date: Thu, 25 Jun 2020 11:18:33 +0800 Subject: [PATCH 4/5] [skip ci] revert unrelated --- python/taichi/misc/image.py | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/python/taichi/misc/image.py b/python/taichi/misc/image.py index 870e5f105cfe2..b3ec797cb557b 100644 --- a/python/taichi/misc/image.py +++ b/python/taichi/misc/image.py @@ -2,7 +2,7 @@ import taichi as ti -def imcook(img): +def imwrite(img, filename): if not isinstance(img, np.ndarray): img = img.to_numpy() @@ -16,37 +16,15 @@ def imcook(img): assert len(img.shape) in [2, 3], "Image must be either RGB/RGBA or greyscale" + resx, resy = img.shape[:2] if len(img.shape) == 2: - img = img.reshape(*img.shape, 1) - - assert img.shape[2] in [1, 3, 4], "Image must be either RGB/RGBA or greyscale" - - return img.swapaxes(0, 1)[::-1, :] - - - -def imdisplay(img): - """ - Try to display image in interactive shell. - """ - if ti.lang.shell.oinspect.name.startswith('IPython'): - import PIL.Image - from io import BytesIO - import IPython.display - import numpy as np - img = imcook(img) - f = BytesIO() - PIL.Image.fromarray(img).save(f, 'png') - IPython.display.display(IPython.display.Image(data=f.getvalue())) + comp = 1 else: - ti.imshow(img) - + comp = img.shape[2] + assert comp in [1, 3, 4], "Image must be either RGB/RGBA or greyscale" -def imwrite(img, filename): - img = imcook(img) - img = np.ascontiguousarray(img) + img = np.ascontiguousarray(img.swapaxes(0, 1)[::-1, :]) ptr = img.ctypes.data - resy, resx, comp = img.shape ti.core.imwrite(filename, ptr, resx, resy, comp) From e55cb5825d47abbbc6113bd882f3055e427406e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E4=BA=8E=E6=96=8C?= <1931127624@qq.com> Date: Fri, 26 Jun 2020 12:03:01 +0800 Subject: [PATCH 5/5] [skip ci] Update docs/syntax.rst Co-authored-by: Yuanming Hu --- docs/syntax.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/syntax.rst b/docs/syntax.rst index 22b7a39191aee..97d00c2f6e4d7 100644 --- a/docs/syntax.rst +++ b/docs/syntax.rst @@ -254,8 +254,7 @@ Debug your program with ``print()`` in Taichi-scope. For example: inside kernel after sync - This is due to the fact that GPU memories are only copied on demand, i.e. - only when the output data is accessed, or performance will be harmed. + This is because GPU memory is only copied when necessary, for performance considerations. Also note that host access or program end will also implicitly invoke for ``ti.sync()``.