From: Georgios Atheridis Date: Thu, 7 Jul 2022 12:32:34 +0000 (+0300) Subject: turning mandelbrot into a package X-Git-Url: https://git.atheridis.org/?a=commitdiff_plain;p=personal%2Fmandelbrot-py.git turning mandelbrot into a package --- diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..40af88a --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +ignore = E203 +max-line-length = 88 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3250ffb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Georgios Atheridis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9705aa2 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +

Mandelbrot Image Renderer

+ +

+ Mandelbrot Image Renderer +

+ +## Installation + +To install the latest version just type `$ pip install git+https://github.com/atheridis/mandelbrot-python.git` +in your terminal. The program has a bunch of different options, type `$ mandelbrot --help` +to see all of the options. + +You may also clone this repository and install it from there. +``` +$ git clone https://github.com/atheridis/mandelbrot-python.git +$ cd mandelbrot-python +$ pip install . +``` + +## How to use + +Just by typing `$ mandelbrot` will create an 1080p image of the mandelbrot set inside +the directory you are currently in. You also have a number of options to choose from. + +* `$ mandelbrot -r 1440` will make the image have a width of 1440 pixels. +* `$ mandelbrot --scale 1` will make the image a square. The default is 16/9 +* `$ mandelbrot -s 100` The depth which each point will check wether it is in the set or not. +* `$ mandelbrot -c -0.745428 0.113009 -l 0.0001` Will center at x=-0.745428 y=0.113009, with the x axis having a total length of 0.0001 +* `$ mandelbrot -r 480 -v -f 350 -z 0.9` Will produce a video of resolution 480p, containing 350 frames (at 24fps) and each frame will reduce the x axis length to 0.9 times the previous. Since this code is running on Python, it is quite slow. A lower quality video is advised. + + +## OLD PROJECT + +This is one of many of my older projects which I have decided to turn it into a package and upload it to github. + diff --git a/mandelbrot.py b/mandelbrot.py deleted file mode 100644 index 8473b9e..0000000 --- a/mandelbrot.py +++ /dev/null @@ -1,118 +0,0 @@ -from numba import njit, prange -import numpy as np -from PIL import Image -import cv2 -import os -import time -import colorsys - - -def hsv2rgb(h,s,v): - return list(round(i * 255) for i in colorsys.hsv_to_rgb(h,s,v)) - -def colour(n): - if n: - hue = n/100 - sat = 1 - value = 1 - return hsv2rgb(hue, sat, value) - return 0 - -@njit(parallel=True, fastmath=True) -def mandelbrot(c, steps): - z = c - for n in prange(steps): - if z.real * z.real + z.imag * z.imag > 4: - return n - z = z*z + c - return 0 - -@njit(parallel=True, fastmath=True) -def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, steps): - x = np.linspace(xmin, xmax, width) - y = np.linspace(ymin, ymax, height) - z = np.empty((width,height)) - - for i in prange(width): - for j in prange(height): - z[i,j] = mandelbrot(x[i] + 1j*y[j], steps) - - return z - -# settings -res = 1080 # height -scale = 16/9 # width / height -steps = 50 # search depth - -# render video -video = False -max_frames = 480 -z_speed = 0.9 # zoom speed - - -center = np.array([0, 0]) # center coordinates -x_length = 4 # length of x axis - - -############################################## -# NOTHING TO EDIT BELOW THIS line -############################################## -axis_length = (x_length, x_length/scale) - -re_axis = (center[0] - axis_length[0] / 2, - center[0] + axis_length[0] / 2) - -im_axis = (center[1] - axis_length[1] / 2, - center[1] + axis_length[1] / 2) - -resolution = (int(scale*res), int(res)) - - -if video: - out = cv2.VideoWriter(f'Mandel0_{time.time()}.avi', - cv2.VideoWriter_fourcc(*'DIV4'), - 24, resolution) - -frame = 1 -while True: - if video: - print(f"On frame {frame} out of {max_frames}") - - c = mandelbrot_set(re_axis[0], re_axis[1], im_axis[0], im_axis[1], resolution[0], resolution[1], steps) - - - data = np.zeros((resolution[1], resolution[0], 3), dtype=np.uint8) - - - for row in range(resolution[1]): - if not video: - print(f"On row {row} out of {resolution[1]}") - for col in range(resolution[0]): - data[row,col] = colour(c[col,row]) - - image = Image.fromarray(data) - - if not video: - image.save(f"i_{center[0]}+{center[1]}i_{steps}_{axis_length[0]}.png") - - if video: - image.save("screen.png") - img = cv2.imread("screen.png") - os.remove("screen.png") - out.write(img) - - axis_length = (z_speed * axis_length[0], - z_speed * axis_length[1]) - - re_axis = (center[0] - axis_length[0] / 2, - center[0] + axis_length[0] / 2) - im_axis = (center[1] - axis_length[1] / 2, - center[1] + axis_length[1] / 2) - - frame += 1 - - if frame > max_frames or not video: - break - -if video: - out.release() diff --git a/mandelbrot/__init__.py b/mandelbrot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mandelbrot/args.py b/mandelbrot/args.py new file mode 100644 index 0000000..94ee941 --- /dev/null +++ b/mandelbrot/args.py @@ -0,0 +1,70 @@ +import argparse + + +def parse_arguments(): + arg_parser = argparse.ArgumentParser( + prog="yorugo-mandelbrot", + description="Generates the mandelbrot set", + ) + + arg_parser.add_argument( + "-r", + "--resolution", + type=int, + default=1080, + help="Set the height of the image or video in pixels. Default: 1080", + ) + arg_parser.add_argument( + "--scale", + type=float, + default=16 / 9, + help="The scale of the image. Width / Height. Default: 16 / 9", + ) + arg_parser.add_argument( + "-s", + "--steps", + type=int, + default=50, + help="The search depth for the mandelbrot set. Default: 50", + ) + + arg_parser.add_argument( + "-v", + "--video", + type=bool, + action=argparse.BooleanOptionalAction, + default=False, + help="Render a video instead of an image.", + ) + arg_parser.add_argument( + "-f", + "--frames", + type=int, + default=500, + help="The number of frames to be rendered into the video. Default: 500", + ) + arg_parser.add_argument( + "-z", + "--zoom-speed", + type=float, + default=0.9, + help="Zoom speed. Default: 0.9", + ) + + arg_parser.add_argument( + "-c", + "--center", + type=float, + nargs=2, + default=(0, 0), + help="Center coordinates. Default 0 0", + ) + arg_parser.add_argument( + "-l", + "--length", + type=float, + default=4, + help="Length of X axis. Default 4", + ) + + return arg_parser.parse_args() diff --git a/mandelbrot/main.py b/mandelbrot/main.py new file mode 100644 index 0000000..b526000 --- /dev/null +++ b/mandelbrot/main.py @@ -0,0 +1,20 @@ +from .args import parse_arguments +from .mandelbrot import compute + + +def main(): + argsv = parse_arguments() + compute( + argsv.resolution, + argsv.scale, + argsv.steps, + argsv.video, + argsv.frames, + argsv.zoom_speed, + argsv.center, + argsv.length, + ) + + +if __name__ == "__main__": + main() diff --git a/mandelbrot/mandelbrot.py b/mandelbrot/mandelbrot.py new file mode 100644 index 0000000..fe5214b --- /dev/null +++ b/mandelbrot/mandelbrot.py @@ -0,0 +1,109 @@ +from numba import jit, prange +import numpy as np +from PIL import Image +import cv2 +import os +import time +import colorsys + + +def hsv2rgb(h, s, v): + return list(round(i * 255) for i in colorsys.hsv_to_rgb(h, s, v)) + + +def colour(n): + if n: + hue = n / 100 + sat = 1 + value = 1 + return hsv2rgb(hue, sat, value) + return 0 + + +@jit +def mandelbrot(c, steps): + z = c + for n in prange(steps): + if z.real * z.real + z.imag * z.imag > 4: + return n + z = z * z + c + return 0 + + +@jit +def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, steps): + x = np.linspace(xmin, xmax, width) + y = np.linspace(ymin, ymax, height) + z = np.empty((width, height)) + + for i in prange(width): + for k in prange(height): + # Reverse row, since computer images start from (0, 0) on the top left + z[i, k] = mandelbrot(x[i] + 1j * y[height - (k + 1)], steps) + + return z + + +def compute(res, scale, steps, video, max_frames, z_speed, center, x_length): + axis_length = (x_length, x_length / scale) + + re_axis = (center[0] - axis_length[0] / 2, center[0] + axis_length[0] / 2) + + im_axis = (center[1] - axis_length[1] / 2, center[1] + axis_length[1] / 2) + + resolution = (int(scale * res), int(res)) + + if video: + out = cv2.VideoWriter( + f"Mandel0_{time.time()}.avi", + cv2.VideoWriter_fourcc(*"DIV4"), + 24, + resolution, + ) + + frame = 1 + while True: + if video: + print(f"On frame {frame} out of {max_frames}") + + c = mandelbrot_set( + re_axis[0], + re_axis[1], + im_axis[0], + im_axis[1], + resolution[0], + resolution[1], + steps, + ) + + data = np.zeros((resolution[1], resolution[0], 3), dtype=np.uint8) + + for row in range(resolution[1]): + if not video: + print(f"On row {row} out of {resolution[1]}") + for col in range(resolution[0]): + data[row, col] = colour(c[col, row]) + + image = Image.fromarray(data) + + if not video: + image.save(f"i_{center[0]}+{center[1]}i_{steps}_{axis_length[0]}.png") + + if video: + image.save("screen.png") + img = cv2.imread("screen.png") + os.remove("screen.png") + out.write(img) + + axis_length = (z_speed * axis_length[0], z_speed * axis_length[1]) + + re_axis = (center[0] - axis_length[0] / 2, center[0] + axis_length[0] / 2) + im_axis = (center[1] - axis_length[1] / 2, center[1] + axis_length[1] / 2) + + frame += 1 + + if frame > max_frames or not video: + break + + if video: + out.release() diff --git a/requirements.txt b/requirements.txt index ed19328..24a55d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,15 @@ -llvmlite==0.35.0 -numba==0.52.0 -numpy==1.19.4 -opencv-python==4.4.0.46 -Pillow==8.0.1 +black==22.6.0 +click==8.1.3 +flake8==4.0.1 +llvmlite==0.38.1 +mccabe==0.6.1 +mypy-extensions==0.4.3 +numba==0.55.2 +numpy==1.22.4 +opencv-python==4.6.0.66 +pathspec==0.9.0 +Pillow==9.2.0 +platformdirs==2.5.2 +pycodestyle==2.8.0 +pyflakes==2.4.0 +tomli==2.0.1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d0d2691 --- /dev/null +++ b/setup.py @@ -0,0 +1,20 @@ +from setuptools import setup, find_packages + +setup( + name="mandelbrot", + version="0.0.1", + author="Georgios Atheridis", + author_email="atheridis@tutamail.com", + packages=find_packages(), + entry_points={ + "console_scripts": [ + "mandelbrot=mandelbrot.main:main", + ], + }, + install_requires=[ + "numba", + "Pillow", + "numpy", + "opencv-python", + ], +)