turning mandelbrot into a package master
authorGeorgios Atheridis <atheridis@tutamail.com>
Thu, 7 Jul 2022 12:32:34 +0000 (15:32 +0300)
committerGeorgios Atheridis <atheridis@tutamail.com>
Thu, 7 Jul 2022 12:32:34 +0000 (15:32 +0300)
.flake8 [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
mandelbrot.py [deleted file]
mandelbrot/__init__.py [new file with mode: 0644]
mandelbrot/args.py [new file with mode: 0644]
mandelbrot/main.py [new file with mode: 0644]
mandelbrot/mandelbrot.py [new file with mode: 0644]
requirements.txt
setup.py [new file with mode: 0644]

diff --git a/.flake8 b/.flake8
new file mode 100644 (file)
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 (file)
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 (file)
index 0000000..9705aa2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+<h1 align="center">Mandelbrot Image Renderer</h1>
+
+<p align="center">
+    <img width="600"
+        alt="Mandelbrot Image Renderer"
+        src="https://imgur.com/M4IIXNp.png">
+</p>
+
+## 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 (file)
index 8473b9e..0000000
+++ /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 (file)
index 0000000..e69de29
diff --git a/mandelbrot/args.py b/mandelbrot/args.py
new file mode 100644 (file)
index 0000000..94ee941
--- /dev/null
@@ -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 (file)
index 0000000..b526000
--- /dev/null
@@ -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 (file)
index 0000000..fe5214b
--- /dev/null
@@ -0,0 +1,109 @@
+from numba import jit, prange\r
+import numpy as np\r
+from PIL import Image\r
+import cv2\r
+import os\r
+import time\r
+import colorsys\r
+\r
+\r
+def hsv2rgb(h, s, v):\r
+    return list(round(i * 255) for i in colorsys.hsv_to_rgb(h, s, v))\r
+\r
+\r
+def colour(n):\r
+    if n:\r
+        hue = n / 100\r
+        sat = 1\r
+        value = 1\r
+        return hsv2rgb(hue, sat, value)\r
+    return 0\r
+\r
+\r
+@jit\r
+def mandelbrot(c, steps):\r
+    z = c\r
+    for n in prange(steps):\r
+        if z.real * z.real + z.imag * z.imag > 4:\r
+            return n\r
+        z = z * z + c\r
+    return 0\r
+\r
+\r
+@jit\r
+def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, steps):\r
+    x = np.linspace(xmin, xmax, width)\r
+    y = np.linspace(ymin, ymax, height)\r
+    z = np.empty((width, height))\r
+\r
+    for i in prange(width):\r
+        for k in prange(height):\r
+            # Reverse row, since computer images start from (0, 0) on the top left\r
+            z[i, k] = mandelbrot(x[i] + 1j * y[height - (k + 1)], steps)\r
+\r
+    return z\r
+\r
+\r
+def compute(res, scale, steps, video, max_frames, z_speed, center, x_length):\r
+    axis_length = (x_length, x_length / scale)\r
+\r
+    re_axis = (center[0] - axis_length[0] / 2, center[0] + axis_length[0] / 2)\r
+\r
+    im_axis = (center[1] - axis_length[1] / 2, center[1] + axis_length[1] / 2)\r
+\r
+    resolution = (int(scale * res), int(res))\r
+\r
+    if video:\r
+        out = cv2.VideoWriter(\r
+            f"Mandel0_{time.time()}.avi",\r
+            cv2.VideoWriter_fourcc(*"DIV4"),\r
+            24,\r
+            resolution,\r
+        )\r
+\r
+    frame = 1\r
+    while True:\r
+        if video:\r
+            print(f"On frame {frame} out of {max_frames}")\r
+\r
+        c = mandelbrot_set(\r
+            re_axis[0],\r
+            re_axis[1],\r
+            im_axis[0],\r
+            im_axis[1],\r
+            resolution[0],\r
+            resolution[1],\r
+            steps,\r
+        )\r
+\r
+        data = np.zeros((resolution[1], resolution[0], 3), dtype=np.uint8)\r
+\r
+        for row in range(resolution[1]):\r
+            if not video:\r
+                print(f"On row {row} out of {resolution[1]}")\r
+            for col in range(resolution[0]):\r
+                data[row, col] = colour(c[col, row])\r
+\r
+        image = Image.fromarray(data)\r
+\r
+        if not video:\r
+            image.save(f"i_{center[0]}+{center[1]}i_{steps}_{axis_length[0]}.png")\r
+\r
+        if video:\r
+            image.save("screen.png")\r
+            img = cv2.imread("screen.png")\r
+            os.remove("screen.png")\r
+            out.write(img)\r
+\r
+            axis_length = (z_speed * axis_length[0], z_speed * axis_length[1])\r
+\r
+            re_axis = (center[0] - axis_length[0] / 2, center[0] + axis_length[0] / 2)\r
+            im_axis = (center[1] - axis_length[1] / 2, center[1] + axis_length[1] / 2)\r
+\r
+            frame += 1\r
+\r
+        if frame > max_frames or not video:\r
+            break\r
+\r
+    if video:\r
+        out.release()\r
index ed19328cc48b606546c0649d9f963b36d7e7069a..24a55d1c05a735a6ee81282196152a8a6addd272 100644 (file)
@@ -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 (file)
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",
+    ],
+)