{"id":17590773,"url":"https://github.com/setanarut/halftonism","last_synced_at":"2025-04-29T09:58:24.757Z","repository":{"id":143399841,"uuid":"221753586","full_name":"setanarut/halftonism","owner":"setanarut","description":"Artistic halftone GIF animation","archived":false,"fork":false,"pushed_at":"2024-02-08T00:43:23.000Z","size":14883,"stargazers_count":22,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-29T09:58:18.331Z","etag":null,"topics":["animated-gif","art","artistic","artistic-coding","color-cycle","color-cycling","computer-graphics","digital-elevation-model","fractal","gif-animation","gif-creator","halftone","halftoning","loop","optical-illusion","procedural-art"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/setanarut.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-11-14T17:38:24.000Z","updated_at":"2025-02-23T15:04:15.000Z","dependencies_parsed_at":"2023-11-11T19:29:42.036Z","dependency_job_id":"2dfb7cbd-b98e-45d2-81f7-4492d53006af","html_url":"https://github.com/setanarut/halftonism","commit_stats":{"total_commits":11,"total_committers":2,"mean_commits":5.5,"dds":0.2727272727272727,"last_synced_commit":"df27db97daa7d1bdfbb3daccc560d2cd326cf6a1"},"previous_names":["setanarut/halftonism"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/setanarut%2Fhalftonism","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/setanarut%2Fhalftonism/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/setanarut%2Fhalftonism/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/setanarut%2Fhalftonism/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/setanarut","download_url":"https://codeload.github.com/setanarut/halftonism/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251480071,"owners_count":21596016,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animated-gif","art","artistic","artistic-coding","color-cycle","color-cycling","computer-graphics","digital-elevation-model","fractal","gif-animation","gif-creator","halftone","halftoning","loop","optical-illusion","procedural-art"],"created_at":"2024-10-22T04:24:37.024Z","updated_at":"2025-04-29T09:58:24.737Z","avatar_url":"https://github.com/setanarut.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Halftonism\n\nArtistic halftone generation library\n\n![example_6_tri_fs2_m70](./assets/eyeswide.gif)\n\n\n## Installation\n\n```shell\npip install git+https://github.com/setanarut/halftonism\n```\n\n## Tutorial\n\n```python\nfrom halftonism import Project\n\np = Project(\"example.ora\", repeat=16, waveform=\"triangle\")\np.save_GIF(\"example.gif\", scale=0.25, miliseconds=70, colors=30, resample=3)\n```\n\n![Krita ORA Screenshot](./example/example.gif)\n\n\n### Preparing the ORA file\n\n![Krita ORA Screenshot](./assets/krita_ORA_SS.jpg)\n\nYou can open the example.ora file in [example](./example/) folder with the [Krita](https://docs.krita.org/en/general_concepts/file_formats/file_ora.html). ([Openraster](https://www.openraster.org/) format). In the example you can see the color layers obtained using [fastLayerDecomposition](https://github.com/CraGL/fastLayerDecomposition) repository. After installing the halftonism package, the `folder2ora` command line tool is installed. Using this, you can convert the folder full of PNG layers obtained with [fastLayerDecomposition](https://github.com/CraGL/fastLayerDecomposition) into an ORA file. Saves it with the name `output.ora`.\n\n```bash\n$ folder2ora ~/Downloads/layers_folder\n\n# saved -\u003e ~/Downloads/layers_folder/output.ora\n```\n\nAlternatively you can decompose and save image as an ORA file with the [decompose](https://github.com/setanarut/decompose) package. [decompose](https://github.com/setanarut/decompose) package separates very quickly using pytorch but the colors are  inaccurate a little bit depending on the palette. \n\n```bash\n$ decomp ~/Desktop/img.png\n# Decomposer mask generation...\n# Decomposer processing alpha layers...\n# Decomposer Done!\n# ORA saved: img.ora\n# 7\n# Palette saved: img_palette.png\n```\n\nAlso you can paint it by hand without layer decompositing. Play around with layer orders and alpha levels for a more color balanced halftone outputs.  \n\n#### ORA Layer Structure\n\nBefore processing the ORA file with Python, you have to follow the template below.\n\n1. Each color layer should contain only a single color.\n\n2. The bottom background layer (base color) must be solid color.\n\n3. There should be a grayscale fractal heightmap at the top layer for halftone patterns. Top layer can be a computer generated heightmaps or [real DEM images](https://tangrams.github.io/heightmapper/) or any suitable grayscale gradient.\n\n#### Some procedural techniques.\n\n- Procedural terrains based on simplified landscape evolution model (LEM) [github.com/TadaTeruki/fastlem-random-terrain](https://github.com/TadaTeruki/fastlem-random-terrain)\n- Diffusion-limited aggregation\n- Mandelbrot Fractals\n- Hydraulic erosion simulation across a heightfield (Noise)\n\n##### Fastlem terrain fractal example\n\n![fastlem](./assets/fastlem.gif)\n\n##### Mandelbrot fractal example\n\n![mandel](./assets/mandel.gif)\n\n\n### ORA processing with halftonism package\n\nHalftone animation is created with 256 pixels linear grayscale gradient [cycling](https://en.wikipedia.org/wiki/Color_cycling), also known as palette shifting or palette animation. The halftone pattern and color layer are mixed with the [*Hard Mix*]([https://](http://www.simplefilter.de/en/basics/mixmods.html)) blending mode (mix *halftone gradient pattern* and *color layer* by 50%. If the alpha value is greater than 128, set the color to 255, otherwise set the color to 0).  \n\n#### `Project()` parameters\n\n##### waveform\n\nWaveform of gradient. `Project()` has parameters `waveform=\"sawtooth\"`, `waveform=\"triangle\"` and `waveform=\"sine\"`. The effects are shown below, respectively.\n\n![downsample](./assets/waveforms.png)\n\n##### repeat\n\nThis number determines how many times gradient will be divided. The formula` (256 / repeat) / frame_skipping` gives the number of animation frames. (frame_skipping is 1 by default, meaning it is disabled). For example, `repeat=8` will give 32 frames. (**256/8/1 = 32 frames**).\n\nLet's save all sawtooth gradient repetitions from 1 to 16 as image. It will help to understand. Height increased from 256x1 to 256x32 and image rotated 90 degree for visibility.\n\n```python\nfrom PIL import Image\nimport numpy as np\nfrom halftonism.utils import gradient_vstack, gradient\n\n\nstack = gradient_vstack(gradient(1, \"sawtooth\"), 32)\nfor repeat in range(2, 17):\n    stack = np.vstack((stack, gradient_vstack(gradient(repeat, \"sawtooth\"), 32)))\nim = Image.fromarray(stack)\nim.rotate(90, expand=True).save(\"gradient_repeats.png\")\n\n```\n\n![Gradient repeats 1-16 ](./assets/gradient_repeats.png)\n\n##### frame_skipping\n\nYou can skip frames to reduce GIF/APNG file size. This also reduces the frame jump effect in repeat numbers where the number 256 is not divisible, such as 3,7,10.\n\n#### Antialiasing\n\nFor antialiasing, you can downscale image with [bicubic sampling](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#PIL.Image.Resampling.BICUBIC). For example, you can start with 2000x2000 and downscale to 500x500 for final output (500 / 2000 = 0.25 scale). The `save_GIF()` `save_APNG()` and `save_frame()` methods have `scale` and [resample](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#PIL.Image.Resampling.BICUBIC) arguments. \n\nResampling example with `NEAREST` on the left and `BICUBIC` on the right. (scale 0.25)\n\n![downsample](./assets/downsample.png)\n\n```python\np.save_GIF(\"output.gif\", scale=0.25, resample=Image.BICUBIC)\np.save_frame(0, \"01_frame.png\", scale=0.25., resample=3)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsetanarut%2Fhalftonism","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsetanarut%2Fhalftonism","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsetanarut%2Fhalftonism/lists"}