 Next up is Martin and he is going to talk about a tool called Pirate for ray tracing and image generation. Martin is a professor of geoinformatics and computer graphics at the Institute of Geo-Matics, Engineering at the University of Applied Sciences, Northwestern Switzerland. You may remember we had the conference there in Basel last year. So, off you go Martin, please start your screen sharing and then excellent, working perfectly. Yes, thank you for the introduction. I just had some technical difficulties, but just solved it in time, perfect. I hope you can see this full screen, perfect. I move this one too. Okay, I'm talking about pirate computer graphics and Jupyter notebooks and the most important part for fun. And also a little bit for teaching. I'm actually teaching Python at the University of Applied Sciences and Arts Northwestern Switzerland. If you attended EuroPython last year and went to the workshops or the social event that was this building, I'm working. And today I'm talking about a small site project. I started creating a couple years ago and still maintain it. And it's basically most for fun to create such graphics we see here. My inspiration or motivation lies back in the early personal computers like here, the Commodore 16 or 64. That's actually from the manual of this computer, so it came out with this ugly basic code and you could create graphics. We saw in the previous talks, also some nice graphics and problem, of course, is always in Python we don't have something built in to create that. I really want something. I can create a couple lines of code and see something. That's my basic motivation. Of course, you could also create something for game development. That's not my focus at the moment. I was in the game development industry many years ago, but at the moment I'm not creating any games anymore. So I have two other things in mind. The first is the server-side graphics generation. I want to have a server that can create some movies, for example, or animations. I also want it for teaching. For example, it's perfect to explain how loops work if you have some graphics or, of course, sorting algorithms. You can display how it works, etc. Also, it's nice to create some real-time content for streaming on Twitch, YouTube or Zoom, whatever. I will show something like that later on. There are many other modules doing the same thing, so I don't really go into these. Just for reference, there are more of these. For 2D graphics, we have many things. For example, Arcade is more for games. Pixel is more for retro games. TV is also for mobile applications. And then there are tons of GUI toolkits where you can create graphics too. And, of course, MoviePie where you can create movies out of still images. The same for 3D graphics. There are some modules. OpenGL would be the biggest overkill. I said I want to create some graphics in a few lines of code. If I would start with OpenGL, I would end up with 200 lines of code just to draw a triangle. That's not really what they want to do at the moment. And, of course, the GUI toolkits could do the same. Also, there are Blender Cinema 4D. You can script using Python. And that's all nice, but now I want to reinvent the wheel. So I created Pirate. It's on GitHub. You can download it. It's a raytracer and an image generator. You need at least Python 3.5. And I had some things in mind. This is what will come in future. It's not yet all working, so I want high quality raytracing in a 2-bit notebook. I want to use it for teaching computer graphics and raytracing concepts. I want to use these concepts to visualize geodata using Python. Including some things like octrees, large point clouds, etc. And also, I have focus on large 3D models. We actually used a modified version of Pirate already to render large 3D models. I'm also not going into that at the moment. And also, we can use OpenStreetMap data creating maps out of it. If you're interested, you can contact me and I can show you how it was done. And of course, the server-side rendering. That's one of the most important things. But of course, the last thing is that I want to have fun programming graphics. So installation is quite easy. I don't have a conda installation yet. I only have a pip installation. You can also use conda, of course, and use pip. And it has no dependencies. So you just install pirate, pip install pyrt. And it would work without any modules, but I highly recommend to have numpy and pillow. If you don't have numpy or pillow, you can still use it. But you can't really create images out of it. So you just can create arrays with RGB values. So at least I recommend numpy and pillow as minimal installation. So let me show you how this works after importing some stuff here. I will explain that in an instant live. You can create a so-called RGB image and that's basically just a virtual frame buffer. So you have this frame buffer where you can put pixels in it. Internally, it's just a numpy array or a Python array if you don't have numpy. And then you can put the pixels in there. You could also do that manually. You could get this array and do your stuff with numpy, for example. But that's not the primary goal. So there are some functions or methods like drawPoint, drawCircle, etc. And you can then draw your things. Internally, it's all done over a vector. So for colors you have a three-dimensional vector with RGB values. Those are between 0 and 1. It's not between 0 and 255. So in theory we can have better quality than 8-bit per pixel. And position is a two-dimensional vector. It would be just a coordinate of our virtual frame buffer here. And then you just call image frame buffer or just call image into bit notebook. And if you have pillow installed, you will see the image. So I could show this using this presentation, but I prefer to show you that live. So let's switch to Jupyter. The source code will be, actually it is already a version there at the moment, but I will update it if I do some changes in the code. So it will be located github.com. Martin Christen, EuroPyson 2020. This link is also in the room on this card. So let me start it by showing you something. I had the link here from github of the project. You can clone this one, for example. I did that. It's an alternative way. If you want to install it using pip, you can install that. Or you can just go to the source code, clone this repository. I already did that. I called it pirate. I start, let me move this one. I start JupyterLab. I could also start a Jupyter notebook. It doesn't really matter. And so far this works. I will go, not really to full screen. Just 125% that's okay. So inside there you would see the source code of pirate. That's here. So that's also if you want to develop something using pirate. You have, for example, the maths. Here you have the vector three class. And you also see the code here. And you can edit the code and you could develop that. And at the end you could make a pull request. And I would, maybe I would allow this one. Okay. The most important thing I have at the moment, no other dependencies. Just nothing. It's pure Python. So what I do is I just create again this virtual frame buffer. So I would just import RGB image from the Pi RT render. And I import the WEC2 and WEC3. And I use the random module to draw some random points. So let me execute that one. For some reasons my keyboard wasn't working. So I create a virtual frame buffer. I do it rather small. You can do what you want here. I create an RGB image 320 to 40. That's a really small resolution. I clear it with black. No, not really black. But I could do completely black, for example. And for some reasons I messed up my keyboard. I have to manually execute that. Okay. So what I do now here is I do a for loop in a range of 5,000. And I draw my points at the position and color. And the position is, as I said before, just a random integer between zero and the width. And also a random integer between zero and the height. This minus one, of course, is because we have one too much from zero to you know that. And color is a random value RGB between zero and one, each component. So we have completely random colors. So I executed that already. And then with image frame buffer I can display that in the Jupyter notebook. And hopefully you can see that and see all the points in different colors. So if you don't see that I could actually increase the point size here. And you see more points now with bigger, bigger size. So I have here some message from Zoom. Okay, that was probably the reason why my keyboard is working. So now it should go again. Perfect. Okay, so what I do now is I draw some lines. I do a range zero to 100. Oh, it's still not working, but it doesn't matter. And you see a live update now. You see the lines are drawn over this image before. It's not working because I created this frame buffer before. And I can use it again. So if I create one frame buffer it's reusable. And you can draw your things inside. Of course, if I want to create more frame buffer I can do that too. And then I have to give each frame buffer a name. So I don't draw to this one. I draw to another one. I will show that in an instant. So I have to set update. And I can specify recommended frames per second, the number of frames per second. You see here I have some debug output. We see it was almost a 30. It's not possible to have this exactly for obvious reasons. But by the way, this doesn't work on Firefox at the moment. That's a known bug. I will fix that at one point. I recommend using Chrome. So I can also load images. I have some images in this folder here. A world map. I can show that quickly here in data. I have some world map. And that's just a JPEG of the world from natural earth. It's public domain dataset. So I don't even have to say from where it is. But I just said it. So no problem. I will show that here with a name world. And from now on, if I want to update that, I have to tell update the frame buffer world. And I do just for fun, I draw a circle here. And we see in the middle, 350, there is a circle. I could also create some random circles, 100. So we see this is actually working. We see these random circles. So don't worry. I will not create a COVID map. We saw enough of those. So I decided to download earthquake data from the USGS. So that's actually geochasing file on the server. And here it's explained what it is. There are tons of metadata inside. And the most important part is the geometry, of course. There you have the longitude, latitude and depth. But we are in 2D. So I don't really care about the elevation of this event and the magnitude of the earthquake. So if it's earthquake seven, that would be not very nice to see. So let's see what that's actually live. I didn't check it this morning. So I don't really know what will happen if there was a bigger earthquake or not. So I will download your old earthquake data from the last two and a half weeks. I download that as file called earthquake geochasing. So let me do that. So there should be a file now, earthquake geochasing, so that worked. And I load it as JSON. This is basic Python. And then I load the image again. And I go through this JSON file. And I get all the coordinates out of it. And I convert the longitude and latitude to pixels that's done here with using the coordinate and converting it to simple pixels and arrange zero to the width and height. And then if the magnitude is bigger than 4.5, I draw a circle. I could reduce that to two, but I don't really want too many earthquakes because there are many small earthquakes happening. And at the end, I display this image. So let's just do that. And we see here are the earthquakes of the past two and a half weeks. Of course, it's not really a nice graphic. You can't really use the mouse pointer and get more information like this. It's really just a plot. This one I could do a server side with a pirate and do an update every hour, for example. That would already work. Okay. In the last talk, we heard a lot about procedural images. This is also one of my favorite topics. I will not explain anymore what's noise. We heard that in the last session. So let me just do a basic introduction to procedural graphics. Again, I create an RGB image. The size is 256 by 256. And then I create some stripes. Let me execute this first. Then I can explain. You see the result is this one. And such stripes can be easily done using a modular function. I have a modular function in pirate inside. You all know with Python, you can also do modular using this person operator. I did this because this modular function uses floats. Of course, Python supports floats too. But you can easily mess up if you don't really use floats, etc. And I did it for readability. So if you prefer this syntax, it's fine. But if you prefer this one, I can read this better. I don't know why. I just don't like this syntax. And this one is better portable to other languages such as C++. So this modular and this 0.5. Let me do a 0.25 just to show you the difference. You see this one influences the width of these lines. And if I make 0.5, I have even black and white and black and white. I could, of course, do other colors. I just set this to black or to white like this. So I can do the other thing. I can use Y instead of X. And then it would just look like that. So you have horizontal lines here. And now the big question is how can we combine that? If I want to make a checkerboard 8 by 8. So this and this, no, it's not and. It's XOR. So we do the same and I do the modular operation here for the vertical and horizontal lines. And in between is an XOR. So if I execute that, you see it's really a checkerboard starting with white, black, white, black and so on. And this already starts XOR. This is a perfect way to explain XOR. By the way, I never understand why Pison doesn't have this one. But it doesn't matter. I do an OR and you see if I do a regular OR, you see I get this pattern because it's this or this. So it's that. If I make an AND, for example, you see the solution is, of course, just the combination of the two. So where the two lines collide. So that's the XOR. And we see here we have the checkerboard. So that's an easy way to understand XOR in a graphical way. So I'm running out of time as expected. It doesn't matter. I create quickly a Mandelbrot image. The formula for the Mandelbrot you can find anywhere, for example on Wikipedia or whatever. And it's a very simple algorithm. You just have to implement this part basically. And you have to hear. This one means that I convert the pixels to a number. So 0, 0 is in the middle and it goes from 0 to 1 and so on. So that's just a conversion. And then I put the result of this while loop to the image. So we see nothing happened because this is just the function. Now I time it and I create this Mandelbrot image. And we see this took 1.5 seconds to create and display it. So that's okay. I remember my first Mandelbrot was a little bit smaller than this. It took about 3 to 4 minutes on a Commodore Amiga end of the 80s. So this is still too slow. So we could use Numbar for example to speed it up. So I just use Numbar, make it do the same here. We see of course this while loop will not be able to break down. But of course we can still try what happens. So I execute that one and then let's time it. We remember before it took 1.5 seconds and now it takes 686 milliseconds. So I execute it again. We see now it only takes, we only have 22. The problem is we have to compile it one time and the second time we execute it it's really fast. So we are at 21 seconds. And so we can also do noise, simplex noise for example. I will go through this a little bit fast. We saw that in the previous talk you can really create many nice things using these simplex noise or noise functions like the purling noise. But one thing I want to mention before I stop for the questions you can also create movies using MoviePyme. Let me just quickly explain that. There is a function called for example MakeFrame. Just a function you can name it like you want. But in there there is a time. And then you draw something and time should be a function of that. So I just draw a circle and I use time to use the radius of the circle. So I do that and now in background it creates this GIF. And if I display that we see this animation. So I can even create animations in the Jupyter Notebook quite simple. But one last thing I do this quickly is the ray tracing part. You can create scenes, you can create some things. And now I start rendering this. This is really pure Python. It takes about 10 seconds. And then it should be finished. Maybe because Zoom runs it takes my CPU power. I see my CPU runs at 30% at the moment. So it took me 12 seconds to create this image here. And this is really pure Python. We also tried this to accelerate this. And we used FireGL in background. And we had this in less than a... We can display this in real time actually using these things. But I want to keep pirate at the moment pure Python. Okay, so thank you very much. And I'm ready for questions if the time still is sufficient. So I don't see any questions in the Q&A of the webinar. But I have a question. Is it possible to run this as part of a web server as well? Because you just focused on the Notebooks. But let's say you want to dynamically generate a GIF or maybe you want to write a bot for Discord, let's say. And you want to produce GIFs that are animated with that work. Yes, of course. You just have to skip this one. This is, of course, this only runs in Jupyter. But for example, if you write something, actually you have what you get out from pirate. If you have NumPy installed, you get a NumPy array, RGB array. So let me show that quickly. This is not the best example, but I do it anyways. So here plus... So I do image data. Actually, I should execute that quickly because... And you see this one is just NumPy array filled with RGB values. It's a Uint8, so it's 8 bit per pixel. Now it's all black because the most things are black at the corner. So you can just save. You can save that. And this is really doable on a server. You can create a service. You don't need a GUI for anything. This is just for, let me say, debug purposes to display this image in Jupyter Notebook. So you could create anything on a web server. Okay, excellent. Thank you. Then now we have two more questions. I think we can take at least one. So first question is, do you need a GPU for ray tracing? No. In the current version, which is on GitHub, we don't have that. We did this in a project. We used Open from AMD. There is some toolkit called OpenFire or something. And they have a ray tracing engine on GPU. And we use that. But the problem is we have then 1000 dependencies. And we have, for example, in Windows, we have two DLS on Mac. For each system, we have different ones. And it's really, really, really messy. So we never released that in GitHub. And for now it's just pure Python. And we don't use any dependencies. So at the moment it's CPU only, this version. Okay. Thank you very much. So there's one more question, but I would like to defer that to the talk channel. And so let's have your applause. Thank you. Thank you. So thank you very much. And now we're going to have a message from our sponsor.