├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .pylintrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── assets │ ├── css │ │ └── mkdocstrings.css │ ├── images │ │ ├── diagrams │ │ │ ├── formats │ │ │ │ └── overview │ │ │ │ │ └── colorization.webp │ │ │ └── styles │ │ │ │ ├── braille │ │ │ │ └── processing.webp │ │ │ │ ├── gradient │ │ │ │ └── processing.webp │ │ │ │ └── overview │ │ │ │ ├── processing.webp │ │ │ │ └── resizing.webp │ │ ├── favicon.webp │ │ ├── logo.png │ │ ├── logo.webp │ │ ├── outputs │ │ │ ├── demo │ │ │ │ ├── apple-braille.webp │ │ │ │ └── apple-gradient.webp │ │ │ ├── draw │ │ │ │ ├── dimensions │ │ │ │ │ ├── zima-h32.webp │ │ │ │ │ ├── zima-term-h.webp │ │ │ │ │ ├── zima-term-w.webp │ │ │ │ │ └── zima-w32.webp │ │ │ │ ├── resample │ │ │ │ │ ├── starry-night-resample-bicubic.webp │ │ │ │ │ ├── starry-night-resample-bilinear.webp │ │ │ │ │ ├── starry-night-resample-box.webp │ │ │ │ │ ├── starry-night-resample-hamming.webp │ │ │ │ │ ├── starry-night-resample-lanczos.webp │ │ │ │ │ └── starry-night-resample-nearest.webp │ │ │ │ └── styles │ │ │ │ │ ├── braille │ │ │ │ │ └── threshold │ │ │ │ │ │ ├── contributions-braille-t0.webp │ │ │ │ │ │ ├── contributions-braille-t108.webp │ │ │ │ │ │ ├── contributions-braille-t168.webp │ │ │ │ │ │ ├── contributions-braille-t210.webp │ │ │ │ │ │ └── contributions-braille-t70.webp │ │ │ │ │ └── gradient │ │ │ │ │ ├── charset │ │ │ │ │ ├── slack-gradient-charset-block.webp │ │ │ │ │ ├── slack-gradient-charset-default.webp │ │ │ │ │ ├── slack-gradient-charset-dot.webp │ │ │ │ │ └── slack-gradient-charset-hash.webp │ │ │ │ │ └── negative │ │ │ │ │ ├── github-gradient-negative.webp │ │ │ │ │ └── github-gradient-normal.webp │ │ │ ├── examples │ │ │ │ ├── 01-image │ │ │ │ │ └── hackerman-gradient.webp │ │ │ │ └── 02-gif │ │ │ │ │ ├── nyan-braille.webp │ │ │ │ │ └── nyan-gradient.webp │ │ │ └── format │ │ │ │ └── colorize │ │ │ │ ├── instagram-color.webp │ │ │ │ └── instagram-gray.webp │ │ └── subjects │ │ │ ├── apple.webp │ │ │ ├── contributions.webp │ │ │ ├── github.webp │ │ │ ├── instagram.webp │ │ │ ├── slack.webp │ │ │ ├── starry-night.webp │ │ │ └── zima.webp │ └── js │ │ └── config.js ├── commands │ ├── draw │ │ ├── braille.md │ │ ├── gradient.md │ │ └── index.md │ ├── index.md │ └── info.md ├── contributing.md ├── examples │ ├── 01-image │ │ ├── hackerman.webp │ │ ├── index.md │ │ └── main.py │ ├── 02-gif │ │ ├── index.md │ │ ├── main.py │ │ └── nyan.webp │ └── 03-web │ │ ├── index.md │ │ └── main.py ├── formats │ ├── ansi.md │ ├── html.md │ └── index.md ├── index.md ├── library │ ├── draw │ │ ├── base.md │ │ ├── braille.md │ │ ├── gradient.md │ │ └── index.md │ ├── format │ │ ├── ansi.md │ │ ├── base.md │ │ ├── html.md │ │ └── index.md │ ├── meta.md │ └── utils.md ├── license.md ├── overrides │ ├── main.html │ └── partials │ │ └── footer.html ├── snippets │ └── cli │ │ ├── draw │ │ ├── braille │ │ │ └── help.txt │ │ ├── gradient │ │ │ └── help.txt │ │ └── help.txt │ │ ├── help.txt │ │ └── info │ │ └── help.txt └── styles │ ├── braille.md │ ├── gradient.md │ └── index.md ├── mkdocs.yml ├── requirements.txt ├── setup.py └── src ├── __init__.py ├── __main__.py ├── cli ├── __init__.py ├── draw │ ├── __init__.py │ ├── braille.py │ └── gradient.py └── info.py ├── data └── logo.txt ├── draw ├── __init__.py ├── base.py ├── braille.py └── gradient.py ├── format ├── __init__.py ├── ansi.py ├── base.py └── html.py ├── meta.py └── utils.py /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | # Environment 8 | env: 9 | CI: true 10 | PYTHON_VERSION: 3.x 11 | 12 | # Jobs to run 13 | jobs: 14 | # Deploy documentation 15 | deploy: 16 | runs-on: ubuntu-latest 17 | steps: 18 | # Checkout source form GitHub 19 | - uses: actions/checkout@v2 20 | 21 | # Install Python runtime and dependencies 22 | - uses: actions/setup-python@v1 23 | with: 24 | python-version: ${{ env.PYTHON_VERSION }} 25 | 26 | # Install package and dependencies 27 | - run: | 28 | pip install -r requirements.txt 29 | pip install . 30 | 31 | # Build documentation 32 | - env: 33 | GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }} 34 | run: | 35 | mkdocs gh-deploy --force 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | build 4 | dist 5 | *.egg-info 6 | 7 | # MkDocs 8 | site/ 9 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [TYPECHECK] 2 | disable= 3 | arguments-differ, 4 | bad-continuation, 5 | too-many-arguments -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing to Picharsso! 4 | 5 | For someone unfamiliar with the code base, 6 | the most efficient way to contribute is 7 | to submit a [feature request](#feature-requests) or [bug report](#bug-reports). 8 | 9 | If you want to dive into the source code, 10 | you can submit a [patch](#patches) as well, 11 | either working on your own ideas or [existing issues][issues]. 12 | 13 | ## Feature Requests 14 | 15 | Do you have an idea for an awesome new feature for Picharsso? 16 | Please [submit a feature request][issue]. 17 | It's great to hear about new ideas! 18 | 19 | If you are inclined to do so, 20 | you're welcome to [fork][fork] Picharsso, 21 | work on implementing the feature yourself, 22 | and submit a patch. 23 | In this case, it's _highly recommended_ that you first [open an issue][issue] 24 | describing your enhancement to get early feedback 25 | on the new feature that you are implementing. 26 | This will help avoid wasted efforts and ensure that your work is incorporated 27 | into the code base. 28 | 29 | ## Bug Reports 30 | 31 | Did something go wrong with Picharsso? 32 | Sorry about that! 33 | Bug reports are greatly appreciated! 34 | 35 | When you [submit a bug report][issue], 36 | please include relevant information 37 | such as Picharsso version, operating system, 38 | error messages, and steps to reproduce the bug. 39 | The more details you can include, 40 | the easier it is to find and fix the bug. 41 | 42 | ## Patches 43 | 44 | Want to hack on Picharsso? Awesome! 45 | 46 | If there are [open issues][issues], 47 | you're more than welcome to work on those - 48 | this is probably the best way to contribute to Picharsso. 49 | If you have your own ideas, that's great too! 50 | In that case, before working on substantial changes to the code base, 51 | it is _highly recommended_ that you first [open an issue][issue] 52 | describing what you intend to work on. 53 | 54 | **Patches are generally submitted as pull requests.** 55 | 56 | Any changes to the code base should 57 | follow the style and coding conventions used in the rest of the project. 58 | The version history should be clean, 59 | and commit messages should be descriptive and [properly formatted][commit-messages]. 60 | 61 | [issue]: https://github.com/kelvindecosta/picharsso/issues/new 62 | [issues]: https://github.com/kelvindecosta/picharsso/issues 63 | [fork]: https://github.com/kelvindecosta/picharsso/fork 64 | [commit-messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kelvin DeCosta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Picharsso 2 | 3 |

4 | 5 | 6 | 7 |
8 | A utility for converting images to text art. 9 |
10 | PyPI - Status 11 | pypi package 12 | GitHub 13 |

14 | 15 |

16 | Installation 17 |    |    18 | Documentation 19 |    |    20 | Examples 21 |    |    22 | Contributing 23 |

24 | 25 | ## Installation 26 | 27 | Run the following command: 28 | 29 | ```bash 30 | pip install picharsso 31 | ``` 32 | 33 | This will: 34 | 35 | - download and install the [`picharsso` Python package](https://pypi.org/project/picharsso/) 36 | (along with its dependencies). 37 | - create an executable, `picharsso`, for the CLI (command line interface). 38 | 39 | > **Verification** 40 | > 41 | > To verify that Picharsso is installed, run: 42 | > 43 | > ```bash 44 | > python -c "import picharsso" 45 | > ``` 46 | 47 | ## Commands 48 | 49 | Picharsso ships with a CLI that provides some basic functionality from the terminal. 50 | 51 | > **Usage** 52 | > 53 | > Run the following command to display a helpful message: 54 | > 55 | > ```bash 56 | > picharsso -h 57 | > ``` 58 | > 59 | > ``` 60 | > Usage: picharsso [options] [args] 61 | > 62 | > A utility for converting images to text art. 63 | > 64 | > Options: 65 | > -h, --help Show this message and exit. 66 | > 67 | > Commands: 68 | > draw Generate text art from an image. 69 | > info Displays package information. 70 | > ``` 71 | 72 | Consider the following image: 73 | 74 |
75 |

76 | Apple logo 77 |

78 | Apple Computer [Rob Janoff, 1977] 79 |
80 | 81 | To convert an image to text art, run: 82 | 83 | ```bash 84 | picharsso draw -c -H 32 gradient 85 | ``` 86 | 87 | Here's what it should look like: 88 | 89 |
90 | Apple logo in text (gradient style) 94 |
95 | 96 | > **Breakdown** 97 | > 98 | > | Argument | Effect | 99 | > | :--------: | :------------------------------------------------------------------------------------ | 100 | > | `-c` | Apply **image colors** to the output text. | 101 | > | `-H 32` | Sets the **number of lines** of the output text to `32`. | 102 | > | `gradient` | Use the [gradient style](https://kelvindecosta.github.io/picharsso/styles/gradient/). | 103 | > 104 | > Don't forget to replace ``. 105 | 106 | Refer to the [CLI documentation](https://kelvindecosta.github.io/picharsso/commands/) to learn about the various **commands** and **arguments**. 107 | 108 | ## Library 109 | 110 | The example from the previous section can be implemented in just a few lines of Python: 111 | 112 | ```python 113 | from PIL import Image 114 | from picharsso import new_drawer 115 | 116 | if __name__ == "__main__": 117 | # Open image 118 | image = Image.open("") 119 | 120 | # Define drawer 121 | drawer = new_drawer("braille", height=32, colorize=True) 122 | 123 | # Print drawer output 124 | print(drawer(image)) 125 | ``` 126 | 127 | Here's what it should look like: 128 | 129 |
130 | Apple logo in text (Braille style) 134 |
135 | 136 | > **Styles** 137 | > 138 | > Refer to the [Styles documentation](https://kelvindecosta.github.io/picharsso/styles/) for an in-depth guide to the **image processing behind Picharsso**. 139 | 140 | Now consider this animated GIF: 141 | 142 |
143 |

144 | Nyan Cat 145 |

146 | Nyan Cat 147 |
148 | 149 | With some more lines of code, you can animate GIFs in text! 150 | 151 | ```python 152 | import time 153 | 154 | from PIL import Image 155 | from picharsso import new_drawer 156 | from picharsso.utils import clear_screen, terminal_size 157 | 158 | 159 | if __name__ == "__main__": 160 | # Open image 161 | image = Image.open("") 162 | 163 | # Get terminal height 164 | height, _ = terminal_size() 165 | 166 | # Define drawer 167 | drawer = new_drawer("braille", height=height, colorize=True, threshold=0) 168 | 169 | # Iterate over frames 170 | texts = [] 171 | for frame_id in range(image.n_frames): 172 | # Select frame 173 | image.seek(frame_id) 174 | 175 | # Save output for frame 176 | texts.append(drawer(image)) 177 | 178 | # Iterate over saved outputs in a circular manner 179 | num_frames = len(texts) 180 | counter = 0 181 | while True: 182 | # Refresh 183 | clear_screen() 184 | 185 | # Print output 186 | print(texts[counter]) 187 | 188 | # Set a delay between frames 189 | time.sleep(1 / num_frames) 190 | 191 | # Circular increment 192 | counter = (counter + 1) % num_frames 193 | ``` 194 | 195 | Here's what it should look like: 196 | 197 |
198 | Nyan Cat in text (Braille style) 202 |
203 | 204 | Refer to the [API documentation](https://kelvindecosta.github.io/picharsso/library/draw/) to learn about the various **classes** and **functions**. 205 | 206 | > **Examples** 207 | > 208 | > Check out some more [examples](https://kelvindecosta.github.io/picharsso/examples/01-image/). 209 | > 210 | > You can use an image [directly from the web](https://kelvindecosta.github.io/picharsso/examples/03-web/) too! 211 | 212 | ## Contributing 213 | 214 | Do you have a feature request, bug report, or patch? Great! 215 | Check out the [contributing guidelines](https://github.com/kelvindecosta/picharsso/blob/master/CONTRIBUTING.md)! 216 | 217 | ## License 218 | 219 | Copyright (c) 2019 Kelvin DeCosta. 220 | Released under the MIT License. 221 | See [LICENSE](https://github.com/kelvindecosta/picharsso/blob/master/LICENSE) for details. 222 | -------------------------------------------------------------------------------- /docs/assets/css/mkdocstrings.css: -------------------------------------------------------------------------------- 1 | div.doc-contents:not(.first) { 2 | padding-left: 25px; 3 | border-left: 4px solid rgba(230, 230, 230); 4 | margin-bottom: 80px; 5 | } 6 | 7 | h5.doc-heading { 8 | text-transform: none !important; 9 | } 10 | 11 | h6.hidden-toc { 12 | margin: 0 !important; 13 | position: relative; 14 | top: -70px; 15 | } 16 | 17 | h6.hidden-toc::before { 18 | margin-top: 0 !important; 19 | padding-top: 0 !important; 20 | } 21 | 22 | h6.hidden-toc a.headerlink { 23 | display: none; 24 | } 25 | 26 | td code { 27 | word-break: normal !important; 28 | } 29 | 30 | td p { 31 | margin-top: 0 !important; 32 | margin-bottom: 0 !important; 33 | } 34 | -------------------------------------------------------------------------------- /docs/assets/images/diagrams/formats/overview/colorization.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/diagrams/formats/overview/colorization.webp -------------------------------------------------------------------------------- /docs/assets/images/diagrams/styles/braille/processing.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/diagrams/styles/braille/processing.webp -------------------------------------------------------------------------------- /docs/assets/images/diagrams/styles/gradient/processing.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/diagrams/styles/gradient/processing.webp -------------------------------------------------------------------------------- /docs/assets/images/diagrams/styles/overview/processing.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/diagrams/styles/overview/processing.webp -------------------------------------------------------------------------------- /docs/assets/images/diagrams/styles/overview/resizing.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/diagrams/styles/overview/resizing.webp -------------------------------------------------------------------------------- /docs/assets/images/favicon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/favicon.webp -------------------------------------------------------------------------------- /docs/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/logo.png -------------------------------------------------------------------------------- /docs/assets/images/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/logo.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/demo/apple-braille.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/demo/apple-braille.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/demo/apple-gradient.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/demo/apple-gradient.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/dimensions/zima-h32.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/dimensions/zima-h32.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/dimensions/zima-term-h.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/dimensions/zima-term-h.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/dimensions/zima-term-w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/dimensions/zima-term-w.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/dimensions/zima-w32.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/dimensions/zima-w32.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/resample/starry-night-resample-bicubic.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/resample/starry-night-resample-bicubic.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/resample/starry-night-resample-bilinear.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/resample/starry-night-resample-bilinear.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/resample/starry-night-resample-box.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/resample/starry-night-resample-box.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/resample/starry-night-resample-hamming.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/resample/starry-night-resample-hamming.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/resample/starry-night-resample-lanczos.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/resample/starry-night-resample-lanczos.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/resample/starry-night-resample-nearest.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/resample/starry-night-resample-nearest.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t0.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t108.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t108.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t168.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t168.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t210.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t210.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t70.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/braille/threshold/contributions-braille-t70.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-block.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-block.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-default.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-default.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-dot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-dot.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-hash.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/gradient/charset/slack-gradient-charset-hash.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/gradient/negative/github-gradient-negative.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/gradient/negative/github-gradient-negative.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/draw/styles/gradient/negative/github-gradient-normal.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/draw/styles/gradient/negative/github-gradient-normal.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/examples/01-image/hackerman-gradient.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/examples/01-image/hackerman-gradient.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/examples/02-gif/nyan-braille.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/examples/02-gif/nyan-braille.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/examples/02-gif/nyan-gradient.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/examples/02-gif/nyan-gradient.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/format/colorize/instagram-color.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/format/colorize/instagram-color.webp -------------------------------------------------------------------------------- /docs/assets/images/outputs/format/colorize/instagram-gray.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/outputs/format/colorize/instagram-gray.webp -------------------------------------------------------------------------------- /docs/assets/images/subjects/apple.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/subjects/apple.webp -------------------------------------------------------------------------------- /docs/assets/images/subjects/contributions.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/subjects/contributions.webp -------------------------------------------------------------------------------- /docs/assets/images/subjects/github.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/subjects/github.webp -------------------------------------------------------------------------------- /docs/assets/images/subjects/instagram.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/subjects/instagram.webp -------------------------------------------------------------------------------- /docs/assets/images/subjects/slack.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/subjects/slack.webp -------------------------------------------------------------------------------- /docs/assets/images/subjects/starry-night.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/subjects/starry-night.webp -------------------------------------------------------------------------------- /docs/assets/images/subjects/zima.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/assets/images/subjects/zima.webp -------------------------------------------------------------------------------- /docs/assets/js/config.js: -------------------------------------------------------------------------------- 1 | window.MathJax = { 2 | tex: { 3 | inlineMath: [["\\(", "\\)"]], 4 | displayMath: [["\\[", "\\]"]], 5 | processEscapes: true, 6 | processEnvironments: true, 7 | }, 8 | options: { 9 | ignoreHtmlClass: ".*|", 10 | processHtmlClass: "arithmatex", 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /docs/commands/draw/braille.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso draw braille - CLI - Picharsso" 3 | description: "Use the Braille style." 4 | --- 5 | 6 | # `picharsso draw braille` 7 | 8 | *Use the [Braille style](../../styles/braille.md).* 9 | 10 | ??? example "Example" 11 | Consider the following image: 12 | 13 |
14 |

15 | Apple logo 16 |

17 |

18 | Apple Computer [Rob Janoff, 1977] 19 |

20 |
21 | 22 | Here's what it should look like: 23 | 24 |
25 | Apple logo in text (Braille style) 29 |
30 | 31 | ## Usage 32 | 33 | ```bash 34 | picharsso draw braille [options] 35 | ``` 36 | 37 | ## Options 38 | 39 | ### `-t`, `--threshold` `INTEGER` `RANGE` 40 | : *Threshold pixel luminance (from grayscale). [default: 64]* 41 | 42 | ??? example 43 | Consider the following image: 44 | 45 |
46 |

47 | Contributions 48 |

49 |

50 | Tiles ressembling GitHub contributions 51 |

52 |
53 | 54 | ```bash 55 | picharsso draw -c -H 32 docs/assets/images/subjects/contributions.webp braille -t 56 | ``` 57 | 58 | Here's what it should look like: 59 | 60 | === "threshold = 0" 61 |
62 | Contributions in text (Braille Threshold 0) 66 |
67 | 68 | === "70" 69 |
70 | Contributions in text (Braille Threshold 70) 74 |
75 | 76 | === "108" 77 |
78 | Contributions in text (Braille Threshold 108) 82 |
83 | 84 | === "168" 85 |
86 | Contributions in text (Braille Threshold 168) 90 |
91 | 92 | === "210" 93 |
94 | Contributions in text (Braille Threshold 210) 98 |
99 | 100 | ### `-h`, `--help` 101 | : *Show this message and exit.* 102 | 103 | ??? abstract "Message" 104 | ``` 105 | --8<-- "docs/snippets/cli/draw/braille/help.txt" 106 | ``` 107 | -------------------------------------------------------------------------------- /docs/commands/draw/gradient.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso draw gradient - CLI - Picharsso" 3 | description: "Use the gradient style." 4 | --- 5 | 6 | # `picharsso draw gradient` 7 | 8 | *Use the [gradient style](../../styles/gradient.md).* 9 | 10 | ??? example "Example" 11 | Consider the following image: 12 | 13 |
14 |

15 | Apple logo 16 |

17 |

18 | Apple Computer [Rob Janoff, 1977] 19 |

20 |
21 | 22 | Here's what it should look like: 23 | 24 |
25 | Apple logo in text (gradient style) 29 |
30 | 31 | ## Usage 32 | 33 | ```bash 34 | picharsso draw gradient [options] 35 | ``` 36 | 37 | ## Options: 38 | 39 | ### `-s`, `--charset` `TEXT` 40 | : *Character set ordered by increasing 'brightness'. [default: :!?PG@]* 41 | 42 | ??? example 43 | Consider the following image: 44 | 45 |
46 |

47 | Slack logo 48 |

49 |

50 | Slack 51 |

52 |
53 | 54 | ```bash 55 | picharsso draw -c -H 32 docs/assets/images/subjects/slack.webp gradient -s 56 | ``` 57 | 58 | Here's what it should look like: 59 | 60 | === "charset = ' :!?PG@' (default)" 61 |
62 | Slack logo in text (default charset) 66 |
67 | 68 | === "'.'" 69 |
70 | Slack logo in text (dot charset) 74 |
75 | 76 | === "'#'" 77 |
78 | Slack logo in text (hash charset) 82 |
83 | 84 | === "'█'" 85 |
86 | Slack logo in text (block charset) 90 |
91 | 92 | ### `-n`, `--negative` 93 | : Whether to invert output text brightness. 94 | 95 | ??? example 96 | Consider the following image: 97 | 98 |
99 |

100 | GitHub logo 101 |

102 |

103 | GitHub 104 |

105 |
106 | 107 | ```bash 108 | picharsso draw -H 32 docs/assets/images/subjects/github.webp gradient [-n] 109 | ``` 110 | 111 | Here's what it should look like: 112 | 113 | === "negative = False" 114 |
115 | GitHub logo in text 119 |
120 | 121 | === "True" 122 |
123 | GitHub logo in text (negative) 127 |
128 | 129 | ### `-h`, `--help` 130 | : *Show this message and exit.* 131 | 132 | ??? abstract "Message" 133 | ``` 134 | --8<-- "docs/snippets/cli/draw/gradient/help.txt" 135 | ``` 136 | -------------------------------------------------------------------------------- /docs/commands/draw/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso draw - CLI - Picharsso" 3 | description: "Generate text art from an image." 4 | --- 5 | 6 | # `picharsso draw` 7 | 8 | *Generate text art from an image.* 9 | 10 | ## Usage 11 | 12 | ``` 13 | picharsso draw [options] [args] 14 | ``` 15 | 16 | ## Arguments 17 | 18 | ### `` 19 | : *Path to the image file.* 20 | 21 | ## Options 22 | 23 | ### `-c`, `--colorize` 24 | : *Apply image colors to output text.* 25 | 26 | ??? example 27 | Consider the following image: 28 | 29 |
30 |

31 | Instagram logo 32 |

33 |

34 | Instagram 35 |

36 |
37 | 38 | ```bash 39 | picharsso draw [-c] -H 32 docs/assets/images/subjects/instagram.webp gradient 40 | ``` 41 | 42 | Here's what it should look like: 43 | 44 | === "colorize = False" 45 |
46 | Instagram logo in text (without color) 50 |
51 | 52 | === "True" 53 |
54 | Instagram logo in text (with color) 58 |
59 | 60 | ### `-m`, `--mode` [`ansi`|`html`] 61 | : *Format mode for output text. [default: ansi]* 62 | 63 | !!! question "Formats" 64 | Refer to the [Formats documentation](../../formats/index.md) 65 | to learn about the supported output formats. 66 | 67 | ### `-r`, `--resample` [`nearest`|`box`|`bilinear`|`hamming`|`bicubic`|`lanczos`] 68 | : *Resampling filter. [default: nearest]* 69 | 70 | ??? example 71 | Consider the following image: 72 | 73 |
74 |

75 | Starry Night 76 |

77 |

78 | Starry Night [Vincent van Gogh, 1889] 79 |

80 |
81 | 82 | ```bash 83 | picharsso draw -c -term-h -r docs/assets/images/subjects/starry-night.webp gradient -s "█" 84 | ``` 85 | 86 | Here's what it should look like: 87 | 88 | === "resample = 'nearest'" 89 |
90 | Starry Night (nearest resampling) 94 |
95 | 96 | === "'box'" 97 |
98 | Starry Night (box resampling) 102 |
103 | 104 | === "'bilinear'" 105 |
106 | Starry Night (bilinear resampling) 110 |
111 | 112 | === "'hamming'" 113 |
114 | Starry Night (hamming resampling) 118 |
119 | 120 | === "'bicubic'" 121 |
122 | Starry Night (bicubic resampling) 126 |
127 | 128 | === "'lanczos'" 129 |
130 | Starry Night (lanczos resampling) 134 |
135 | 136 | ### `-H`, `--height` `INTEGER` 137 | : *Height of output text in characters.* 138 | *If 0, derives from width. [default: 0]* 139 | 140 | !!! info "Lines" 141 | `height` is the number of lines in the text output. 142 | 143 | ??? example 144 | Consider the following image: 145 | 146 |
147 |

148 | Zima Blue 149 |

150 |

151 | Zima Blue [Zima] 152 |

153 |
154 | 155 | ```bash 156 | picharsso draw -c -H 32 docs/assets/images/subjects/zima.webp gradient 157 | ``` 158 | 159 | Here's what it should look like: 160 | 161 |
162 | Zima Blue (with height = 32) 166 |
167 | 168 | ### `-W`, `--width` `INTEGER` 169 | : *Width of output text in characters.* 170 | *If 0, derives from height. [default: 0]* 171 | 172 | !!! info "Characters per line" 173 | `width` is the number of characters (including whitespace) per line in the text output. 174 | 175 | ??? example 176 | Consider the following image: 177 | 178 |
179 |

180 | Zima Blue 181 |

182 |

183 | Zima Blue [Zima] 184 |

185 |
186 | 187 | ```bash 188 | picharsso draw -c -W 32 docs/assets/images/subjects/zima.webp gradient 189 | ``` 190 | 191 | Here's what it should look like: 192 | 193 |
194 | Zima Blue (with width = 32) 198 |
199 | 200 | ### `-term-h`, `--terminal-height` 201 | : *Sets height to terminal height.* 202 | 203 | ??? example 204 | Consider the following image: 205 | 206 |
207 |

208 | Zima Blue 209 |

210 |

211 | Zima Blue [Zima] 212 |

213 |
214 | 215 | ```bash 216 | picharsso draw -c -term-h docs/assets/images/subjects/zima.webp gradient 217 | ``` 218 | 219 | Here's what it should look like: 220 | 221 |
222 | Zima Blue (with terminal height) 226 |
227 | 228 | ??? bug 229 | When used while [piping](https://en.wikipedia.org/wiki/Pipeline_(Unix)){target=_blank}, 230 | `height` is set to the default terminal height, 231 | which is usually `24`. 232 | 233 | ### `-term-w`, `--terminal-width` 234 | : *Sets width to terminal width.* 235 | 236 | ??? example 237 | Consider the following image: 238 | 239 |
240 |

241 | Zima Blue 242 |

243 |

244 | Zima Blue [Zima] 245 |

246 |
247 | 248 | ```bash 249 | picharsso draw -c -term-w docs/assets/images/subjects/zima.webp gradient 250 | ``` 251 | 252 | Here's what it should look like: 253 | 254 |
255 | Zima Blue (with terminal width) 259 |
260 | 261 | ??? bug 262 | When used while [piping](https://en.wikipedia.org/wiki/Pipeline_(Unix)){target=_blank}, 263 | `width` is set to the default terminal width, 264 | which is usually `80`. 265 | 266 | ### `-h`, `--help` 267 | : *Show this message and exit.* 268 | 269 | ??? abstract "Message" 270 | ``` 271 | --8<-- "docs/snippets/cli/draw/help.txt" 272 | ``` 273 | 274 | ## Subcommands 275 | 276 | !!! question "Styles" 277 | Refer to the [Styles documentation](../../styles/index.md) 278 | for an in-depth guide to the **image processing behind Picharsso**. 279 | 280 | ### [`braille`](braille.md) 281 | : Use the [Braille style](../../styles/braille.md). 282 | 283 | ### [`gradient`](gradient.md) 284 | : Use the [gradient style](../../styles/gradient.md). 285 | 286 | -------------------------------------------------------------------------------- /docs/commands/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso - CLI - Picharsso" 3 | description: "A utility for converting images to text art." 4 | --- 5 | 6 | # `picharsso` 7 | 8 | *A utility for converting images to text art.* 9 | 10 | ## Usage 11 | 12 | ```bash 13 | picharsso [options] [args] 14 | ``` 15 | 16 | ## Options 17 | 18 | ### `-h`, `--help` 19 | : *Show this message and exit.* 20 | 21 | ??? abstract "Message" 22 | ``` 23 | --8<-- "docs/snippets/cli/help.txt" 24 | ``` 25 | 26 | ## Subcommands 27 | 28 | ### [`draw`](draw/index.md) 29 | : *Generate text art from an image.* 30 | 31 | ### [`info`](info.md) 32 | : *Displays package information.* 33 | -------------------------------------------------------------------------------- /docs/commands/info.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso info - CLI - Picharsso" 3 | description: "Displays package information." 4 | --- 5 | 6 | # `picharsso info` 7 | 8 | *Displays package information.* 9 | 10 | ## Usage 11 | 12 | ```bash 13 | picharsso info [options] 14 | ``` 15 | 16 | ## Options 17 | 18 | ### `-h`, `--help` 19 | : *Show this message and exit.* 20 | 21 | ??? abstract "Message" 22 | ``` 23 | --8<-- "docs/snippets/cli/info/help.txt" 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Contributing - Picharsso" 3 | description: "Contributing Guidelines" 4 | --- 5 | 6 | --8<-- "CONTRIBUTING.md" 7 | -------------------------------------------------------------------------------- /docs/examples/01-image/hackerman.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/examples/01-image/hackerman.webp -------------------------------------------------------------------------------- /docs/examples/01-image/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Image - Examples - Picharsso" 3 | description: "This example illustrates how to covert an image to text art." 4 | --- 5 | 6 | # Image 7 | 8 | This example illustrates how to covert an image to text art. 9 | 10 | !!! abstract "Source" 11 | ```python linenums="1" 12 | --8<-- "docs/examples/01-image/main.py" 13 | ``` 14 | 15 | ??? success "Result" 16 | Consider the following image: 17 | 18 |
19 |

20 | Hackerman 21 |

22 |

23 | Elliot Anderson is Hackerman [u/JBisBlu] 24 |

25 |
26 | 27 | The output of the above script should look like this: 28 | 29 |
30 | Hackerman in text (gradient style) 34 |
35 | 36 | -------------------------------------------------------------------------------- /docs/examples/01-image/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 name> -*- 3 | 4 | """This script converts an image to text art.""" 5 | 6 | from pathlib import Path 7 | 8 | from PIL import Image 9 | from picharsso import new_drawer 10 | from picharsso.utils import terminal_size 11 | 12 | if __name__ == "__main__": 13 | # Choose image 14 | # image_path = "" 15 | image_path = Path(__file__).parent / "hackerman.webp" 16 | 17 | # Open image 18 | image = Image.open(image_path) 19 | 20 | # Choose an art style 21 | style = "gradient" # or "braille" 22 | 23 | # Set height 24 | height, _ = terminal_size() 25 | 26 | # Define drawer 27 | drawer = new_drawer(style, height=height, colorize=True) 28 | 29 | # Print drawer output 30 | print(drawer(image)) 31 | -------------------------------------------------------------------------------- /docs/examples/02-gif/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "GIF - Examples - Picharsso" 3 | description: "This example illustrates how to animate a GIF image in text art." 4 | --- 5 | 6 | # GIF 7 | 8 | This example illustrates how to animate a GIF image in text art. 9 | 10 | !!! abstract "Source" 11 | ```python linenums="1" 12 | --8<-- "docs/examples/02-gif/main.py" 13 | ``` 14 | 15 | ??? success "Result" 16 | Consider the following image: 17 | 18 | 19 |
20 |

21 | Nyan Cat 22 |

23 |

24 | Nyan Cat 25 |

26 |
27 | 28 | The output of the above script should look like this: 29 | 30 | 31 |
32 | Nyan Cat in text (gradient style) 36 |
37 | -------------------------------------------------------------------------------- /docs/examples/02-gif/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 name> -*- 3 | 4 | """This script animates a GIF image in text art.""" 5 | 6 | from pathlib import Path 7 | import time 8 | 9 | from PIL import Image 10 | from picharsso import new_drawer 11 | from picharsso.utils import clear_screen, terminal_size 12 | 13 | 14 | if __name__ == "__main__": 15 | # Choose image 16 | # image_path = "" 17 | image_path = Path(__file__).parent / ("nyan.webp") 18 | 19 | # Open image 20 | image = Image.open(image_path) 21 | 22 | # Get terminal height 23 | height, _ = terminal_size() 24 | 25 | # Choose an art style 26 | style = "gradient" # or "braille" 27 | 28 | # Define drawer 29 | drawer = new_drawer(style, height=height, colorize=True) 30 | 31 | # Iterate over frames 32 | texts = [] 33 | for frame_id in range(image.n_frames): 34 | # Select frame 35 | image.seek(frame_id) 36 | 37 | # Save output for frame 38 | texts.append(drawer(image)) 39 | 40 | # Iterate over saved outputs in a circular manner 41 | num_frames = len(texts) 42 | counter = 0 43 | while True: 44 | # Refresh 45 | clear_screen() 46 | 47 | # Print output 48 | print(texts[counter]) 49 | 50 | # Set a delay between frames 51 | time.sleep(1 / num_frames) 52 | 53 | # Circular increment 54 | counter = (counter + 1) % num_frames 55 | -------------------------------------------------------------------------------- /docs/examples/02-gif/nyan.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelvindecosta/picharsso/2e73095c7cb230174bbf5a0242b65247f9ab08ad/docs/examples/02-gif/nyan.webp -------------------------------------------------------------------------------- /docs/examples/03-web/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Web - Examples - Picharsso" 3 | description: "This example illustrates how to animate a GIF image, from the web, in text art." 4 | --- 5 | 6 | # Web 7 | 8 | This example illustrates how to animate a GIF image, from the web, in text art. 9 | 10 | ??? tip "Try it yourself!" 11 | *¡Apagando las luces!* 12 | 13 | !!! abstract "Source" 14 | ```python linenums="1" 15 | --8<-- "docs/examples/03-web/main.py" 16 | ``` 17 | 18 | !!! note 19 | Although this example uses an animated GIF as input, 20 | the same principle can be applied to static images from the web. 21 | 22 | !!! warning 23 | This example requires the [`requests` library](https://requests.readthedocs.io/en/master/){target=_blank}. 24 | -------------------------------------------------------------------------------- /docs/examples/03-web/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 name> -*- 3 | 4 | """This script animates a GIF image, from the web, in text art.""" 5 | 6 | from io import BytesIO 7 | import time 8 | 9 | from PIL import Image 10 | from picharsso import new_drawer 11 | from picharsso.utils import clear_screen, terminal_size 12 | 13 | import requests 14 | 15 | 16 | if __name__ == "__main__": 17 | # Set URL of image 18 | image_url = "https://bit.ly/3hs2Vxr" 19 | 20 | # Open Image from respose content 21 | response = requests.get(image_url) 22 | image = Image.open(BytesIO(response.content)) 23 | 24 | # Get terminal height 25 | height, _ = terminal_size() 26 | 27 | # Choose an art style 28 | style = "gradient" # or "braille" 29 | 30 | # Define drawer 31 | drawer = new_drawer(style, height=height, colorize=True) 32 | 33 | # Iterate over frames 34 | texts = [] 35 | for frame_id in range(image.n_frames): 36 | # Select frame 37 | image.seek(frame_id) 38 | 39 | # Save output for frame 40 | texts.append(drawer(image)) 41 | 42 | # Iterate over saved outputs in a circular manner 43 | num_frames = len(texts) 44 | counter = 0 45 | while True: 46 | # Refresh 47 | clear_screen() 48 | 49 | # Print output 50 | print(texts[counter]) 51 | 52 | # Set a delay between frames 53 | time.sleep(1 / num_frames) 54 | 55 | # Circular increment 56 | counter = (counter + 1) % num_frames 57 | -------------------------------------------------------------------------------- /docs/formats/ansi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ANSI - Formats - Picharsso" 3 | description: "The ANSI format" 4 | --- 5 | 6 | # ANSI 7 | 8 | This format supports [ANSI Escape Codes](https://en.wikipedia.org/wiki/ANSI_escape_code){target=_blank}. 9 | 10 | !!! info "Default" 11 | Since it can be used to create **plain text documents**, 12 | this format is chosen as the **default**. 13 | 14 | ## Procedure 15 | 16 | This format is implemented by the [`AnsiFormatter`][picharsso.format.ansi.AnsiFormatter]. 17 | 18 | !!! question "Formatting" 19 | Refer to the [procedure](./index.md#procedure) outlined in the Formats documentation 20 | for an overview of the **steps common to all formats**. 21 | 22 | ### Translation 23 | 24 | This format doesn't require any translation. 25 | 26 | ??? abstract "Source" 27 | Refer to the [`translate` function][picharsso.format.ansi.AnsiFormatter.translate] 28 | for more information. 29 | 30 | ### Colorization 31 | 32 | Using the [`sty` Python library](https://sty.mewo.dev/){target=_blank}, 33 | color is applied to the elements of the `text_matrix`. 34 | 35 | ??? abstract "Source" 36 | Refer to the [`color` function][picharsso.format.ansi.AnsiFormatter.color] 37 | for more information. 38 | 39 | ### Unification 40 | 41 | Elements of each row of the `text_matrix` are joined to form 42 | lines, which are further joined to form one huge string of text. 43 | 44 | ??? abstract "Source" 45 | Refer to the [`unify` function][picharsso.format.ansi.AnsiFormatter.unify] 46 | for more information. 47 | -------------------------------------------------------------------------------- /docs/formats/html.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "HTML - Formats - Picharsso" 3 | description: "The HTML format" 4 | --- 5 | 6 | # HTML 7 | 8 | This format supports [HTML](https://en.wikipedia.org/wiki/HTML){target=_blank}. 9 | 10 | ## Procedure 11 | 12 | This format is implemented by the [`HtmlFormatter`][picharsso.format.html.HtmlFormatter]. 13 | 14 | !!! question "Formatting" 15 | Refer to the [procedure](./index.md#procedure) outlined in the Formats documentation 16 | for an overview of the **steps common to all formats**. 17 | 18 | ### Translation 19 | 20 | This format requires some characters to be translated 21 | to their equivalent [HTML character entities](https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references){target=_blank}. 22 | 23 | ??? abstract "Source" 24 | Refer to the [`translate` function][picharsso.format.html.HtmlFormatter.translate] 25 | for more information. 26 | 27 | ### Colorization 28 | 29 | Color is applied to each element in the `text_matrix` by 30 | wrapping it in a styled `` element. 31 | 32 | ??? abstract "Source" 33 | Refer to the [`color` function][picharsso.format.html.HtmlFormatter.color] 34 | for more information. 35 | 36 | ### Unification 37 | 38 | Elements of each row of the `text_matrix` are joined to form 39 | lines. 40 | All lines are wrapped in a `
` elemen each. 41 | The entire text output is wrapped in a `
` element. 42 | 43 | ??? abstract "Source" 44 | Refer to the [`unify` function][picharsso.format.html.HtmlFormatter.unify] 45 | for more information. 46 | -------------------------------------------------------------------------------- /docs/formats/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Formats - Picharsso" 3 | description: "An overview of the output formats of Picharsso" 4 | --- 5 | 6 | # Formats 7 | 8 | After an `image` is coverted to a `text_matrix`, 9 | it must be formatted before it can be output. 10 | 11 | ## Procedure 12 | 13 | There are steps involved in this process that are common to 14 | all `formatters`. 15 | Picharsso defines a [`BaseFormatter`][picharsso.format.base.BaseFormatter] 16 | that abstracts this general procedure. 17 | 18 | ### Initialization 19 | 20 | This step assigns values to the parameters for the algorithms. 21 | 22 | #### Color 23 | 24 | The `colorize` parameter controls whether the output text must include the colors from the image. 25 | 26 | Consider the following image: 27 | 28 |
29 |

30 | Instagram logo 31 |

32 |

33 | Instagram 34 |

35 |
36 | 37 | Here's what it should look like: 38 | 39 | === "colorize = False" 40 |
41 | Instagram logo in text (without color) 45 |
46 | 47 | === "True" 48 |
49 | Instagram logo in text (with color) 53 |
54 | 55 | #### Vectorization 56 | 57 | The `vcolor` attribute is a vectorized version of the `color` method. 58 | 59 | ### Translation 60 | 61 | The elements of the `text_matrix` are encoded in the Unicode standard. 62 | 63 | Depending on the output format, these characters must be translated accordingly. 64 | 65 | ??? abstract "Source" 66 | Refer to the [`translate` function][picharsso.format.base.BaseFormatter.translate] for more information. 67 | 68 | ### Colorization 69 | 70 | Colors are pooled from the original `image` by resizing it to the dimensions of the output text. 71 | This ensures that each character has a unique pixel, and thus, a unique color. 72 | 73 | With the vectorized `color` method, `vcolor`, the elements of the `text_matrix` 74 | are transformed into strings of text that represent 75 | the original character as well as its color. 76 | 77 |
78 | Text matrix colorization 79 |
80 | 81 | ??? abstract "Source" 82 | Refer to the [`color` function][picharsso.format.base.BaseFormatter.color] for more information. 83 | 84 | ### Unification 85 | 86 | Finally, the `text_matrix` is unified into a single string of text. 87 | This text, when viewed through a means supporting the particular format, 88 | should look like the original image. 89 | 90 | ??? abstract "Source" 91 | Refer to the [`unify` function][picharsso.format.base.BaseFormatter.unify] for more information. 92 | 93 | ## Varities 94 | 95 | All the following formats are implemented by a `formatter` 96 | which inherits from the [`BaseFormatter`][picharsso.format.base.BaseFormatter]. 97 | 98 | ### ANSI 99 | : The [ANSI format](ansi.md) is implemented by the [`AnsiFormater`][picharsso.format.ansi.AnsiFormatter]. 100 | 101 | ### HTML 102 | : The [HTML format](html.md) is implemented by the [`HtmlFormater`][picharsso.format.html.HtmlFormatter]. 103 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Picharsso 2 | 3 |
4 |

5 | Picharsso 6 |

7 |

8 | A utility for converting images to text art. 9 |

10 |
11 | 12 | ## Installation 13 | 14 | Run the following command: 15 | 16 | ```bash 17 | pip install picharsso 18 | ``` 19 | 20 | This will: 21 | 22 | * download and install the [`picharsso` Python package](https://pypi.org/project/picharsso/){target=_blank} 23 | (along with its dependencies). 24 | * create an executable, `picharsso`, for the CLI (command line interface). 25 | 26 | ??? success "Verification" 27 | To verify that Picharsso is installed, run: 28 | 29 | ```bash 30 | python -c "import picharsso" 31 | ``` 32 | 33 | ## Commands (CLI) 34 | 35 | Picharsso ships with a CLI that provides some basic functionality from the terminal. 36 | 37 | ??? question "Usage" 38 | Run the following command to display a helpful message: 39 | 40 | ```bash 41 | picharsso -h 42 | ``` 43 | 44 | ``` 45 | --8<-- "docs/snippets/cli/help.txt" 46 | ``` 47 | 48 | Consider the following image: 49 | 50 |
51 |

52 | Apple logo 53 |

54 |

55 | Apple Computer [Rob Janoff, 1977] 56 |

57 |
58 | 59 | To convert an image to text art, run: 60 | 61 | === "Braille" 62 | ```bash 63 | picharsso draw -c -H 32 braille 64 | ``` 65 | 66 | Here's what it should look like: 67 | 68 |
69 | Apple logo in text (Braille style) 73 |
74 | 75 | !!! abstract "Breakdown" 76 | | Argument | Effect | 77 | | :-------: | :------------------------------------------------------- | 78 | | `-c` | Apply **image colors** to the output text. | 79 | | `-H 32` | Sets the **number of lines** of the output text to `32`. | 80 | | `braille` | Use the [Braille style](styles/braille.md). | 81 | 82 | === "Gradient" 83 | ```bash 84 | picharsso draw -c -H 32 gradient 85 | ``` 86 | 87 | Here's what it should look like: 88 | 89 |
90 | Apple logo in text (gradient style) 94 |
95 | 96 | !!! abstract "Breakdown" 97 | | Argument | Effect | 98 | | :--------: | :------------------------------------------------------- | 99 | | `-c` | Apply **image colors** to the output text. | 100 | | `-H 32` | Sets the **number of lines** of the output text to `32`. | 101 | | `gradient` | Use the [gradient style](styles/gradient.md). | 102 | 103 | !!! warning 104 | Don't forget to replace ``. 105 | 106 | !!! question "CLI" 107 | Refer to the [CLI documentation](commands/index.md) 108 | to learn about the various **commands** and **arguments**. 109 | 110 | ## Library (API) 111 | 112 | The example from the previous section can be implemented in just a few lines of Python: 113 | 114 | === "Braille" 115 | ```python linenums="1" hl_lines="8-12" 116 | from PIL import Image 117 | from picharsso import new_drawer 118 | 119 | if __name__ == "__main__": 120 | # Open image 121 | image = Image.open("") 122 | 123 | # Define drawer 124 | drawer = new_drawer("braille", height=32, colorize=True) 125 | 126 | # Print drawer output 127 | print(drawer(image)) 128 | ``` 129 | 130 | === "Gradient" 131 | ```python linenums="1" hl_lines="8-12" 132 | from PIL import Image 133 | from picharsso import new_drawer 134 | 135 | if __name__ == "__main__": 136 | # Open image 137 | image = Image.open("") 138 | 139 | # Define drawer 140 | drawer = new_drawer("gradient", height=32, colorize=True) 141 | 142 | # Print drawer output 143 | print(drawer(image)) 144 | ``` 145 | 146 | !!! info "Pillow" 147 | Picharsso integrates well with [Pillow](https://python-pillow.org/){target=_blank}, 148 | the friendly PIL fork. 149 | 150 | !!! question "Styles" 151 | Refer to the [Styles documentation](styles/index.md) 152 | for an in-depth guide to the **image processing behind Picharsso**. 153 | 154 | Now consider this animated GIF: 155 | 156 |
157 |

158 | Nyan Cat 159 |

160 |

161 | Nyan Cat 162 |

163 |
164 | 165 | With some more lines of code, you can animate GIFs in text! 166 | 167 | === "Braille" 168 | ```python linenums="1" hl_lines="15-16 24-25" 169 | import time 170 | 171 | from PIL import Image 172 | from picharsso import new_drawer 173 | from picharsso.utils import clear_screen, terminal_size 174 | 175 | 176 | if __name__ == "__main__": 177 | # Open image 178 | image = Image.open("") 179 | 180 | # Get terminal height 181 | height, _ = terminal_size() 182 | 183 | # Define drawer 184 | drawer = new_drawer("braille", height=height, colorize=True, threshold=0) 185 | 186 | # Iterate over frames 187 | texts = [] 188 | for frame_id in range(image.n_frames): 189 | # Select frame 190 | image.seek(frame_id) 191 | 192 | # Save output for frame 193 | texts.append(drawer(image)) 194 | 195 | # Iterate over saved outputs in a circular manner 196 | num_frames = len(texts) 197 | counter = 0 198 | while True: 199 | # Refresh 200 | clear_screen() 201 | 202 | # Print output 203 | print(texts[counter]) 204 | 205 | # Set a delay between frames 206 | time.sleep(1 / num_frames) 207 | 208 | # Circular increment 209 | counter = (counter + 1) % num_frames 210 | ``` 211 | 212 | Here's what it should look like: 213 | 214 |
215 | Nyan Cat in text (Braille style) 219 |
220 | 221 | === "Gradient" 222 | ```python linenums="1" hl_lines="15-16 24-25" 223 | import time 224 | 225 | from PIL import Image 226 | from picharsso import new_drawer 227 | from picharsso.utils import clear_screen, terminal_size 228 | 229 | 230 | if __name__ == "__main__": 231 | # Open image 232 | image = Image.open("") 233 | 234 | # Get terminal height 235 | height, _ = terminal_size() 236 | 237 | # Define drawer 238 | drawer = new_drawer("gradient", height=height, colorize=True) 239 | 240 | # Iterate over frames 241 | texts = [] 242 | for frame_id in range(image.n_frames): 243 | # Select frame 244 | image.seek(frame_id) 245 | 246 | # Save output for frame 247 | texts.append(drawer(image)) 248 | 249 | # Iterate over saved outputs in a circular manner 250 | num_frames = len(texts) 251 | counter = 0 252 | while True: 253 | # Refresh 254 | clear_screen() 255 | 256 | # Print output 257 | print(texts[counter]) 258 | 259 | # Set a delay between frames 260 | time.sleep(1 / num_frames) 261 | 262 | # Circular increment 263 | counter = (counter + 1) % num_frames 264 | ``` 265 | 266 | Here's what it should look like: 267 | 268 |
269 | Nyan Cat in text (gradient style) 273 |
274 | 275 | !!! question "API" 276 | Refer to the [API documentation](library/draw/index.md) 277 | to learn about the various **classes** and **functions**. 278 | 279 | !!! tip "Examples" 280 | Check out some more [examples](examples/01-image/index.md). 281 | 282 | You can use an image [directly from the web](examples/03-web/index.md) too! 283 | -------------------------------------------------------------------------------- /docs/library/draw/base.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.draw.base - API - Picharsso" 3 | description: "This module defines an abstract base formatter." 4 | --- 5 | 6 | # `base` 7 | 8 | ::: picharsso.draw.base 9 | -------------------------------------------------------------------------------- /docs/library/draw/braille.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.draw.braille - API - Picharsso" 3 | description: "This module defines a drawer for Braille style." 4 | --- 5 | 6 | # `braille` 7 | 8 | ::: picharsso.draw.braille 9 | -------------------------------------------------------------------------------- /docs/library/draw/gradient.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.draw.gradient - API - Picharsso" 3 | description: "This module defines a drawer for gradient style." 4 | --- 5 | 6 | # `gradient` 7 | 8 | ::: picharsso.draw.gradient 9 | -------------------------------------------------------------------------------- /docs/library/draw/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.draw - API - Picharsso" 3 | description: "This package defines drawers for different styles of text art." 4 | --- 5 | 6 | # `draw` 7 | 8 | ::: picharsso.draw.__init__ 9 | -------------------------------------------------------------------------------- /docs/library/format/ansi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.format.ansi - API - Picharsso" 3 | description: "This module defines a formatter for the ANSI coloring scheme." 4 | --- 5 | 6 | # `ansi` 7 | 8 | ::: picharsso.format.ansi 9 | -------------------------------------------------------------------------------- /docs/library/format/base.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.format.base - API - Picharsso" 3 | description: "This module defines an abstract base formatter." 4 | --- 5 | 6 | # `base` 7 | 8 | ::: picharsso.format.base 9 | -------------------------------------------------------------------------------- /docs/library/format/html.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.format.html - API - Picharsso" 3 | description: "This module defines a formatter for HTML." 4 | --- 5 | 6 | # `html` 7 | 8 | ::: picharsso.format.html 9 | -------------------------------------------------------------------------------- /docs/library/format/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.format - API - Picharsso" 3 | description: "This package defines formatters for different modes of text output." 4 | --- 5 | 6 | # `format` 7 | 8 | ::: picharsso.format.__init__ 9 | -------------------------------------------------------------------------------- /docs/library/meta.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.meta - API - Picharsso" 3 | description: "This module defines variables for the package metadata." 4 | --- 5 | 6 | # `meta` 7 | 8 | ::: picharsso.meta 9 | -------------------------------------------------------------------------------- /docs/library/utils.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "picharsso.utils - API - Picharsso" 3 | description: "This module defines utility functions that are used across the package." 4 | --- 5 | 6 | # `utils` 7 | 8 | ::: picharsso.utils 9 | -------------------------------------------------------------------------------- /docs/license.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "License - Picharsso" 3 | description: "Package License" 4 | --- 5 | 6 | # License 7 | 8 | ``` 9 | --8<-- "LICENSE" 10 | ``` -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | {#- 2 | This file was automatically generated - do not edit 3 | -#} 4 | {% extends "base.html" %} 5 | {% block extrahead %} 6 | {% set image = config.site_url ~ config.extra.site_image %} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /docs/overrides/partials/footer.html: -------------------------------------------------------------------------------- 1 | {% import "partials/language.html" as lang with context %} 2 | 3 | 4 |
5 | 27 |
28 | -------------------------------------------------------------------------------- /docs/snippets/cli/draw/braille/help.txt: -------------------------------------------------------------------------------- 1 | Usage: picharsso draw braille [options] 2 | 3 | Use the Braille style. 4 | 5 | Options: 6 | -t, --threshold INTEGER RANGE Threshold pixel luminance (from grayscale). 7 | [default: 64] 8 | 9 | -h, --help Show this message and exit. 10 | -------------------------------------------------------------------------------- /docs/snippets/cli/draw/gradient/help.txt: -------------------------------------------------------------------------------- 1 | Usage: picharsso draw gradient [options] 2 | 3 | Use the gradient style. 4 | 5 | Options: 6 | -s, --charset TEXT Character set ordered by increasing 'brightness'. 7 | [default: :!?PG@] 8 | 9 | -n, --negative Whether to invert output text brightness. 10 | -h, --help Show this message and exit. 11 | -------------------------------------------------------------------------------- /docs/snippets/cli/draw/help.txt: -------------------------------------------------------------------------------- 1 | Usage: picharsso draw [options] [args] 2 | 3 | Generate text art from an image. 4 | 5 | Path to the image file. 6 | 7 | Options: 8 | -c, --colorize Apply image colors to output text. 9 | -m, --mode [ansi|html] Format mode for output text. [default: 10 | ansi] 11 | 12 | -r, --resample [nearest|box|bilinear|hamming|bicubic|lanczos] 13 | Resampling filter. [default: nearest] 14 | -H, --height INTEGER Height of output text in characters. 15 | 16 | If 0, derives from width. [default: 0] 17 | 18 | -W, --width INTEGER Width of output text in characters. 19 | 20 | If 0, derives from height. [default: 0] 21 | 22 | -term-h, --terminal-height Sets height to terminal height. 23 | -term-w, --terminal-width Sets width to terminal width. 24 | -h, --help Show this message and exit. 25 | 26 | Commands: 27 | braille Use the Braille style. 28 | gradient Use the gradient style. 29 | -------------------------------------------------------------------------------- /docs/snippets/cli/help.txt: -------------------------------------------------------------------------------- 1 | Usage: picharsso [options] [args] 2 | 3 | A utility for converting images to text art. 4 | 5 | Options: 6 | -h, --help Show this message and exit. 7 | 8 | Commands: 9 | draw Generate text art from an image. 10 | info Displays package information. 11 | -------------------------------------------------------------------------------- /docs/snippets/cli/info/help.txt: -------------------------------------------------------------------------------- 1 | Usage: picharsso info [options] 2 | 3 | Displays package information. 4 | 5 | Options: 6 | -h, --help Show this message and exit. 7 | -------------------------------------------------------------------------------- /docs/styles/braille.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Braille - Styles - Picharsso" 3 | description: "The Braille based text art style" 4 | --- 5 | 6 | # Braille 7 | 8 | This style uses the characters of the [Braille writing system](https://en.wikipedia.org/wiki/Braille){target=_blank}. 9 | 10 | ??? example "Example" 11 | Consider the following image: 12 | 13 |
14 |

15 | Apple logo 16 |

17 |

18 | Apple Computer [Rob Janoff, 1977] 19 |

20 |
21 | 22 | Here's what it should look like: 23 | 24 |
25 | Apple logo in text (Braille style) 29 |
30 | 31 | !!! info "Encoding" 32 | Traditional Braille characters are made up of `6` dots (⠿). 33 | Since each dot could be in one of `2` states (raised or lowered), 34 | there are a total of `64` unique combinations. 35 | 36 | In [Unicode](https://en.wikipedia.org/wiki/Unicode){target=_blank}, braille is represented in a block, 37 | the [Braille Patterns](https://en.wikipedia.org/wiki/Braille_Patterns){target=_blank}. 38 | There are `256` unique characters each in its own 8-dot cell (⣿). 39 | 40 | ## Procedure 41 | 42 | This style is implemented using the [`BrailleDrawer`][picharsso.draw.braille.BrailleDrawer]. 43 | 44 | !!! question "Styling" 45 | Refer to the [procedure](./index.md#procedure) outlined in the Styles documentation 46 | for an overview of the **steps common to all styles**. 47 | 48 | ### Initialization 49 | 50 | #### Threshold 51 | 52 | The `threshold` parameter **filters out pixels** of the input image 53 | whose **grayscale intensities are lesser** than it. 54 | 55 | Consider the following image: 56 | 57 |
58 |

59 | Contributions 60 |

61 |

62 | Tiles ressembling GitHub contributions 63 |

64 |
65 | 66 | Here's what it should look like: 67 | 68 | === "threshold = 0" 69 |
70 | Contributions in text (Braille Threshold 0) 74 |
75 | 76 | === "70" 77 |
78 | Contributions in text (Braille Threshold 70) 82 |
83 | 84 | === "108" 85 |
86 | Contributions in text (Braille Threshold 108) 90 |
91 | 92 | === "168" 93 |
94 | Contributions in text (Braille Threshold 168) 98 |
99 | 100 | === "210" 101 |
102 | Contributions in text (Braille Threshold 210) 106 |
107 | 108 | #### Matrices 109 | 110 | The `kernel` attribute holds a NumPy [`ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html){target=_blank} 111 | containing the following matrix: 112 | 113 | $$ 114 | kernel = 115 | \begin{bmatrix} 116 | 1 & 8\\ 117 | 2 & 16\\ 118 | 4 & 32\\ 119 | 64 & 128\end{bmatrix} 120 | $$ 121 | 122 | The Unicode encoding of the 8-dot cell Braille system 123 | is done by assigning each of the dots a power of `2`. 124 | Each character in the Braille Patterns block has a unique Unicode value 125 | that is obtained by summing these powers. 126 | 127 | The `charset_array` attribute holds another NumPy [`ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html){target=_blank} 128 | containing all 256 Braille characters. 129 | 130 | ### Conversion 131 | 132 | #### Resizing 133 | 134 | Assuming the output text should have the dimensions `text_height` and `text_width`, 135 | the image must be resized according to the following criteria: 136 | 137 | * `image_height = 4 * text_height`. 138 | * `image_width = 2 * text_width`. 139 | * If either `image_height` or `image_width` is `0`, 140 | it is derived from the other by preserving the aspect ratio of the original image. 141 | 142 | Following the above algorithm, **each pixel** of the resized `image` 143 | will be **assigned to one dot** (Braille character dot) in the output text. 144 | 145 | ??? abstract "Source" 146 | Refer to the [`calculate_size` function][picharsso.draw.braille.BrailleDrawer.calculate_size] 147 | for more information. 148 | 149 | #### Processing 150 | 151 | 1. The resized `image` is first converted to its grayscale. 152 | 2. Each pixel is set to either `0` or `1` based on whether 153 | its grayscale intensity is below or above the `threshold`. 154 | 3. A convolution operation is performed on this filtered image 155 | using the `kernel` matrix. 156 | The resultant matrix has the ofsetted Unicode values for 157 | the corresponding Braille character. 158 | 4. The `charset_array` is indexed with the resultant "indices" matrix, 159 | giving the final `text_matrix`. 160 | 161 |
162 | Processing an image into a text matrix (Braille style) 163 |
164 | 165 | ??? abstract "Source" 166 | Refer to the [`process` function][picharsso.draw.braille.BrailleDrawer.process] 167 | for more information. 168 | -------------------------------------------------------------------------------- /docs/styles/gradient.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Gradient - Styles - Picharsso" 3 | description: "The gradient based text art style" 4 | --- 5 | 6 | # Gradient 7 | 8 | This style uses [Unicode characters](https://en.wikipedia.org/wiki/Unicode){target=_blank}. 9 | 10 | ??? example "Example" 11 | Consider the following image: 12 | 13 |
14 |

15 | Apple logo 16 |

17 |

18 | Apple Computer [Rob Janoff, 1977] 19 |

20 |
21 | 22 | Here's what it should look like: 23 | 24 |
25 | Apple logo in text (gradient style) 29 |
30 | 31 | ## Procedure 32 | 33 | This style is implemented using the [`GradientDrawer`][picharsso.draw.gradient.GradientDrawer]. 34 | 35 | !!! question "Styling" 36 | Refer to the [procedure](./index.md#procedure) outlined in the Styles documentation 37 | for an overview of the **steps common to all styles**. 38 | 39 | ### Initialization 40 | 41 | #### Charset 42 | 43 | The `charset` parameter is a string containing characters **ordered by their 44 | perceived brightness**. 45 | 46 | Consider the following image: 47 | 48 |
49 |

50 | Slack logo 51 |

52 |

53 | Slack 54 |

55 |
56 | 57 | Here's what it should look like: 58 | 59 | === "charset = ' :!?PG@' (default)" 60 |
61 | Slack logo in text (default charset) 65 |
66 | 67 | === "'.'" 68 |
69 | Slack logo in text (dot charset) 73 |
74 | 75 | === "'#'" 76 |
77 | Slack logo in text (hash charset) 81 |
82 | 83 | === "'█'" 84 |
85 | Slack logo in text (block charset) 89 |
90 | 91 | #### Negative 92 | 93 | The `negative` parameter controls whether the `charset` must be **reversed**. 94 | 95 | Consider the following image: 96 | 97 |
98 |

99 | GitHub logo 100 |

101 |

102 | GitHub 103 |

104 |
105 | 106 | Here's what it should look like: 107 | 108 | === "negative = False" 109 |
110 | GitHub logo in text 114 |
115 | 116 | === "True" 117 |
118 | GitHub logo in text (negative) 122 |
123 | 124 | #### Matrices 125 | 126 | The `charset_array` attribute holds a NumPy [`ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html){target=_blank} 127 | containing all the characters in the `charset`. 128 | 129 | ### Conversion 130 | 131 | #### Resizing 132 | 133 | Assuming the output text should have the dimensions `text_height` and `text_width`, 134 | the image must be resized according to the following criteria: 135 | 136 | * `image_height = text_height`. 137 | * `image_width = text_width`. 138 | * If either `image_height` or `image_width` is `0`, 139 | it is derived from the other by preserving the aspect ratio of the original image. 140 | 141 | Following the above algorithm, **each pixel** of the resized `image` 142 | will be assigned to **one character** in the output text. 143 | 144 | ??? abstract "Source" 145 | Refer to the [`calculate_size` function][picharsso.draw.gradient.GradientDrawer.calculate_size] 146 | for more information. 147 | 148 | #### Processing 149 | 150 | 1. The resized `image` is first converted to its grayscale. 151 | 2. The image matrix is normalized such that the grayscale range shifts from `(0, 255)` to `(0, len(charset))`. 152 | 3. The `charset_array` is indexed with the resultant "indices" matrix, 153 | giving the final `text_matrix`. 154 | 155 |
156 | Processing an image into a text matrix (gradient style) 157 |
158 | 159 | ??? abstract "Source" 160 | Refer to the [`process` function][picharsso.draw.gradient.GradientDrawer.process] 161 | for more information. 162 | -------------------------------------------------------------------------------- /docs/styles/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Styles - Picharsso" 3 | description: "An overview of the styles of Picharsso" 4 | --- 5 | 6 | # Styles 7 | 8 | Styles refer to the different image processing algorithms 9 | that are performed on input images. 10 | **They affect how images look when viewed in the text form**. 11 | 12 | ## Procedure 13 | 14 | Although the different styles use algorithms that produce different results, 15 | there are many *common*, yet fundamental, steps involved in the entire process. 16 | Picharsso defines a [`BaseDrawer`][picharsso.draw.base.BaseDrawer] 17 | that abstracts this general procedure. 18 | 19 | ### Initialization 20 | 21 | This step **assigns values to the parameters** for the algorithms. 22 | 23 | #### Dimensions 24 | 25 | Picharsso provides control over the **dimensions of the output text** 26 | with the `height` and `width` parameters. 27 | 28 | Consider the following image: 29 | 30 |
31 |

32 | Zima Blue 33 |

34 |

35 | Zima Blue [Zima] 36 |

37 |
38 | 39 | Here's what it should look like: 40 | 41 | === "height = 32" 42 |
43 | Zima Blue (with height = 32) 47 |
48 | 49 | === "terminal height" 50 |
51 | Zima Blue (with terminal height) 55 |
56 | 57 | === "width = 32" 58 |
59 | Zima Blue (with width = 32) 63 |
64 | 65 | === "terminal width" 66 |
67 | Zima Blue (with terminal width) 71 |
72 | 73 | !!! info "Preserving Aspect Ratio" 74 | The relationship between `height` and `width` preserves the aspect ratio of the input image. 75 | 76 | *When either one of the dimensions is set as `0`, it derives its value from the other.* 77 | 78 | ??? error 79 | Assigning `0` to both `height` and `width` raises an error. 80 | Atleast one of the dimensions must be assigned a non-zero positive integer. 81 | 82 | #### Resampling Filter 83 | 84 | There are instances when an input image must be **scaled to an appropriate size** 85 | before it can be used as input for an algorithm. 86 | During this resizing process, pixels must sampled/ resampled 87 | to generate the new, resized, image. 88 | 89 | Picharsso uses the [resampling filters that come with Pillow](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#filters){target=_blank}. 90 | The choice of the resampling filter is defined by the `resample` parameter. 91 | 92 | Consider the following image: 93 | 94 |
95 |

96 | Starry Night 97 |

98 |

99 | Starry Night [Vincent van Gogh, 1889] 100 |

101 |
102 | 103 | Here's what it should look like: 104 | 105 | === "resample = 'nearest'" 106 |
107 | Starry Night (nearest resampling) 111 |
112 | 113 | === "'box'" 114 |
115 | Starry Night (box resampling) 119 |
120 | 121 | === "'bilinear'" 122 |
123 | Starry Night (bilinear resampling) 127 |
128 | 129 | === "'hamming'" 130 |
131 | Starry Night (hamming resampling) 135 |
136 | 137 | === "'bicubic'" 138 |
139 | Starry Night (bicubic resampling) 143 |
144 | 145 | === "'lanczos'" 146 |
147 | Starry Night (lanczos resampling) 151 |
152 | 153 | !!! note 154 | All resizing operations use the same filter that is set by `resample`. 155 | 156 | ### Normalization 157 | 158 | Pillow supports multiple [image modes](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes){target=_blank}. 159 | For simplicity, the algorithms were designed to work on the `RGB` image mode. 160 | Hence, images must be converted appropriately. 161 | 162 | !!! info "White Background" 163 | A white background is applied to images with the `P` and `RGBA` modes. 164 | 165 | ??? abstract "Source" 166 | Refer to the [`ensure_rgb` function][picharsso.utils.ensure_rgb] for more information. 167 | 168 | ### Conversion 169 | 170 | This step lies at the heart of each style. 171 | 172 | #### Resizing 173 | 174 | Before the `image` can be processed, it must be resized appropriately. 175 | The **scale** of the resizing **depends on the processing algorithm**. 176 | 177 |
178 | Image resizing 179 |
180 | 181 | ??? abstract "Source" 182 | Refer to the [`calculate_size` function][picharsso.draw.base.BaseDrawer.calculate_size] 183 | for more information. 184 | 185 | #### Processing 186 | 187 | The resized `image` is processed into a `text_matrix`. 188 | 189 |
190 | Processing an image into a text matrix 191 |
192 | 193 | ??? abstract "Source" 194 | Refer to the [`process` function][picharsso.draw.base.BaseDrawer.process] 195 | for more information. 196 | 197 | ### Formatting 198 | 199 | Before it can be displayed, the `text_matrix` must be **formatted into a single string**. 200 | The type of `formatter` used is defined by the `mode` parameter. 201 | 202 | !!! info "Colorization" 203 | The `formatter` requires the original `image` and the choice of `resample` filter 204 | for pooling colors. 205 | 206 | Refer to the [colorization step](../formats/index.md#colorization) 207 | for more information. 208 | 209 | !!! question "Formats" 210 | Refer to the [Formats documentation](../formats/index.md) 211 | to learn about the supported output formats. 212 | 213 | ## Varieties 214 | 215 | All the following styles are implemented using a `drawer` 216 | which inherits from the [`BaseDrawer`][picharsso.draw.base.BaseDrawer]. 217 | 218 | Consider the following image: 219 | 220 |
221 |

222 | Apple logo 223 |

224 |

225 | Apple Computer [Rob Janoff, 1977] 226 |

227 |
228 | 229 | Here's what it should look like: 230 | 231 | ### Braille 232 | : The [Braille style](braille.md) is implemented using the 233 | [`BrailleDrawer`][picharsso.draw.braille.BrailleDrawer]. 234 | 235 |
236 | Apple logo in text (Braille style) 240 |
241 | 242 | ### Gradient 243 | : The [gradient style](gradient.md) is implemented using the 244 | [`GradientDrawer`][picharsso.draw.gradient.GradientDrawer]. 245 | 246 |
247 | Apple logo in text (gradient style) 251 |
-------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Picharsso 2 | site_description: "A utility for converting images to text art." 3 | site_author: "Kelvin DeCosta" 4 | site_url: https://kelvindecosta.github.io/picharsso/ 5 | 6 | repo_name: kelvindecosta/picharsso 7 | repo_url: https://github.com/kelvindecosta/picharsso 8 | edit_uri: "" 9 | 10 | copyright: Copyright © 2019 Kelvin DeCosta 11 | 12 | nav: 13 | - Home: index.md 14 | - Styles: 15 | - Overview: styles/index.md 16 | - Braille: styles/braille.md 17 | - Gradient: styles/gradient.md 18 | - Formats: 19 | - Overview: formats/index.md 20 | - ANSI: formats/ansi.md 21 | - HTML: formats/html.md 22 | - CLI: 23 | - picharsso: commands/index.md 24 | - picharsso draw: commands/draw/index.md 25 | - picharsso draw braille: commands/draw/braille.md 26 | - picharsso draw gradient: commands/draw/gradient.md 27 | - picharsso info: commands/info.md 28 | - API: 29 | - draw: 30 | - __init__: library/draw/index.md 31 | - base: library/draw/base.md 32 | - braille: library/draw/braille.md 33 | - gradient: library/draw/gradient.md 34 | - format: 35 | - __init__: library/format/index.md 36 | - base: library/format/base.md 37 | - ansi: library/format/ansi.md 38 | - html: library/format/html.md 39 | - meta: library/meta.md 40 | - utils: library/utils.md 41 | - Examples: 42 | - Image: examples/01-image/index.md 43 | - GIF: examples/02-gif/index.md 44 | - Web: examples/03-web/index.md 45 | - License: license.md 46 | - Contributing: contributing.md 47 | 48 | theme: 49 | name: material 50 | custom_dir: docs/overrides 51 | logo: assets/images/favicon.webp 52 | palette: 53 | primary: black 54 | accent: light blue 55 | font: 56 | text: Nunito 57 | code: Source Code Pro 58 | favicon: assets/images/favicon.webp 59 | include_search_page: false 60 | search_index_only: true 61 | 62 | markdown_extensions: 63 | - admonition 64 | - attr_list 65 | - codehilite: 66 | guess_lang: false 67 | - def_list 68 | - meta 69 | - pymdownx.betterem: 70 | smart_enable: all 71 | - pymdownx.details 72 | - pymdownx.arithmatex: 73 | generic: true 74 | - pymdownx.smartsymbols 75 | - pymdownx.snippets: 76 | check_paths: true 77 | - pymdownx.superfences 78 | - pymdownx.tabbed 79 | - toc: 80 | permalink: "#" 81 | 82 | plugins: 83 | - search 84 | - mkdocstrings: 85 | handlers: 86 | python: 87 | rendering: 88 | show_root_heading: false 89 | show_root_toc_entry: false 90 | - exclude: 91 | glob: 92 | - snippets/* 93 | - minify: 94 | minify_html: true 95 | 96 | extra: 97 | social: 98 | - icon: fontawesome/solid/globe 99 | link: https://kelvindecosta.com 100 | name: "Website" 101 | - icon: fontawesome/brands/github 102 | link: https://github.com/kelvindecosta 103 | name: "GitHub" 104 | - icon: fontawesome/brands/linkedin 105 | link: https://linkedin.com/in/kelvindecosta 106 | name: "LinkedIn" 107 | - icon: fontawesome/brands/instagram 108 | link: https://instagram.com/_kelvindecosta 109 | name: "Instagram" 110 | - icon: fontawesome/brands/twitter 111 | link: https://twitter.com/_kelvindecosta 112 | name: "Twitter" 113 | - icon: fontawesome/solid/envelope 114 | link: https://mailhide.io/e/c1R8e 115 | name: "Mail" 116 | site_image: assets/images/logo.png 117 | 118 | extra_css: 119 | - assets/css/mkdocstrings.css 120 | 121 | extra_javascript: 122 | - assets/js/config.js 123 | - https://polyfill.io/v3/polyfill.min.js?features=es6 124 | - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js 125 | 126 | google_analytics: 127 | - !!python/object/apply:os.getenv ["GOOGLE_ANALYTICS_KEY"] 128 | - auto 129 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # For package 2 | click 3 | numpy 4 | pillow 5 | sty 6 | 7 | # For examples 8 | requests 9 | 10 | # For repository 11 | mkdocs 12 | mkdocs-exclude 13 | mkdocs-material 14 | mkdocs-minify-plugin 15 | mkdocstrings -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | from src.meta import NAME, DESCRIPTION, VERSION, REPO_URL, DOCS_URL, AUTHOR, LICENSE 4 | 5 | with open("README.md", "r") as fs: 6 | LONGE_DESCRIPTION = fs.read() 7 | 8 | setup( 9 | name=NAME, 10 | version=VERSION, 11 | description=DESCRIPTION, 12 | long_description=LONGE_DESCRIPTION, 13 | long_description_content_type="text/markdown", 14 | project_urls={"Documentation": DOCS_URL, "Source": REPO_URL}, 15 | author=AUTHOR, 16 | author_email="decostakelvin@gmail.com", 17 | license=LICENSE, 18 | classifiers=[ 19 | "Programming Language :: Python :: 3", 20 | "Operating System :: OS Independent", 21 | "License :: OSI Approved :: MIT License", 22 | "Development Status :: 5 - Production/Stable", 23 | "Topic :: Artistic Software", 24 | "Topic :: Terminals", 25 | "Topic :: Utilities", 26 | ], 27 | packages=list(map(lambda x: x.replace("src", NAME), find_packages("."))), 28 | package_dir={NAME: "src"}, 29 | package_data={"": ["data/*.txt"]}, 30 | python_requires=">=3.8", 31 | install_requires=["click", "numpy", "pillow", "sty",], 32 | entry_points={"console_scripts": [f"{NAME} = {NAME}.cli:main"]}, 33 | ) 34 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | from .draw import new_drawer 2 | from .format import new_formatter 3 | from .meta import DESCRIPTION as __doc__, VERSION as __version__ 4 | 5 | __all__ = ["new_drawer", "new_formatter"] 6 | -------------------------------------------------------------------------------- /src/__main__.py: -------------------------------------------------------------------------------- 1 | """This module defines the behaviour of the package 2 | in a top-level script environment. 3 | 4 | The following command executes this script: 5 | 6 | ```bash 7 | python -m picharsso 8 | ``` 9 | """ 10 | 11 | from .cli import main 12 | 13 | if __name__ == "__main__": 14 | main() 15 | -------------------------------------------------------------------------------- /src/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """This package defines the `picharsso` command. 2 | 3 | Refer to https://kelvindecosta.github.io/picharsso/commands/. 4 | """ 5 | 6 | 7 | import click 8 | 9 | from .draw import draw 10 | from .info import info 11 | from ..meta import DESCRIPTION 12 | 13 | CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) 14 | 15 | 16 | @click.group( 17 | help=DESCRIPTION, 18 | options_metavar="[options]", 19 | subcommand_metavar=" [args]", 20 | context_settings=CONTEXT_SETTINGS, 21 | ) 22 | def main(): 23 | """The main program.""" 24 | 25 | 26 | main.add_command(draw) 27 | main.add_command(info) 28 | -------------------------------------------------------------------------------- /src/cli/draw/__init__.py: -------------------------------------------------------------------------------- 1 | """This package defines the `picharsso draw` command. 2 | 3 | Refer to https://kelvindecosta.github.io/picharsso/commands/draw/. 4 | """ 5 | 6 | import click 7 | from PIL import Image 8 | 9 | from ...draw import RESAMPLING_FILTERS, DEFAULT_RESAMPLING 10 | from ...format import FORMATTERS, DEFAULT_FORMATTER 11 | from ...utils import terminal_size 12 | 13 | from .gradient import draw_gradient 14 | from .braille import draw_braille 15 | 16 | 17 | @click.group(options_metavar="[options]", subcommand_metavar=" [args]") 18 | @click.argument("path", type=click.Path(exists=True), metavar="") 19 | @click.option( 20 | "-c", "--colorize", is_flag=True, help="Apply image colors to output text." 21 | ) 22 | @click.option( 23 | "-m", 24 | "--mode", 25 | type=click.Choice(list(FORMATTERS.keys())), 26 | default=DEFAULT_FORMATTER, 27 | help="Format mode for output text.", 28 | show_default=True, 29 | ) 30 | @click.option( 31 | "-r", 32 | "--resample", 33 | type=click.Choice(list(RESAMPLING_FILTERS.keys())), 34 | default=DEFAULT_RESAMPLING, 35 | help="Resampling filter.", 36 | show_default=True, 37 | ) 38 | @click.option( 39 | "-H", 40 | "--height", 41 | type=int, 42 | default=0, 43 | help="Height of output text in characters.\n\nIf 0, derives from width.", 44 | show_default=True, 45 | ) 46 | @click.option( 47 | "-W", 48 | "--width", 49 | type=int, 50 | default=0, 51 | help="Width of output text in characters.\n\nIf 0, derives from height.", 52 | show_default=True, 53 | ) 54 | @click.option( 55 | "-term-h", 56 | "--terminal-height", 57 | is_flag=True, 58 | help="Sets height to terminal height.", 59 | ) 60 | @click.option( 61 | "-term-w", "--terminal-width", is_flag=True, help="Sets width to terminal width.", 62 | ) 63 | @click.pass_context 64 | def draw( 65 | context, 66 | path, 67 | colorize, 68 | mode, 69 | resample, 70 | height, 71 | width, 72 | terminal_height, 73 | terminal_width, 74 | ): 75 | """Generate text art from an image. 76 | 77 | Path to the image file. 78 | """ 79 | image = Image.open(path) 80 | 81 | if terminal_width or terminal_height or height == 0 and width == 0: 82 | term_h, term_w = terminal_size() 83 | 84 | if terminal_height: 85 | height = term_h 86 | 87 | if terminal_width: 88 | width = term_w 89 | 90 | if height == 0 and width == 0: 91 | height = term_h 92 | width = term_w 93 | 94 | context.obj = { 95 | "image": image, 96 | "colorize": colorize, 97 | "mode": mode, 98 | "resample": resample, 99 | "height": height, 100 | "width": width, 101 | } 102 | 103 | 104 | draw.add_command(draw_gradient) 105 | draw.add_command(draw_braille) 106 | -------------------------------------------------------------------------------- /src/cli/draw/braille.py: -------------------------------------------------------------------------------- 1 | """This module defines the `picharsso draw braille` command. 2 | 3 | Refer to https://kelvindecosta.github.io/picharsso/commands/draw/braille/. 4 | """ 5 | 6 | 7 | import click 8 | 9 | from ...draw import new_drawer 10 | from ...draw.braille import DEFAULT_THRESHOLD 11 | 12 | 13 | @click.command("braille", options_metavar="[options]") 14 | @click.option( 15 | "-t", 16 | "--threshold", 17 | type=click.IntRange(0, 255), 18 | help="Threshold pixel luminance (from grayscale).", 19 | default=DEFAULT_THRESHOLD, 20 | show_default=True, 21 | ) 22 | @click.pass_context 23 | def draw_braille(context, threshold): 24 | """Use the Braille style.""" 25 | 26 | image = context.obj.pop("image") 27 | 28 | drawer = new_drawer("braille", threshold=threshold, **context.obj) 29 | print(drawer(image)) 30 | -------------------------------------------------------------------------------- /src/cli/draw/gradient.py: -------------------------------------------------------------------------------- 1 | """This module defines the `picharsso draw gradient` command. 2 | 3 | Refer to https://kelvindecosta.github.io/picharsso/commands/draw/gradient/. 4 | """ 5 | 6 | import click 7 | 8 | from ...draw import new_drawer 9 | from ...draw.gradient import DEFAULT_CHARSET 10 | 11 | 12 | @click.command("gradient", options_metavar="[options]") 13 | @click.option( 14 | "-s", 15 | "--charset", 16 | type=str, 17 | help="Character set ordered by increasing 'brightness'.", 18 | default=DEFAULT_CHARSET, 19 | show_default=True, 20 | ) 21 | @click.option( 22 | "-n", "--negative", is_flag=True, help="Whether to invert output text brightness." 23 | ) 24 | @click.pass_context 25 | def draw_gradient(context, charset, negative): 26 | """Use the gradient style.""" 27 | 28 | image = context.obj.pop("image") 29 | 30 | drawer = new_drawer("gradient", charset=charset, negative=negative, **context.obj) 31 | print(drawer(image)) 32 | -------------------------------------------------------------------------------- /src/cli/info.py: -------------------------------------------------------------------------------- 1 | """This module defines the `picharsso info` command. 2 | 3 | Refer to https://kelvindecosta.github.io/picharsso/commands/info/. 4 | """ 5 | 6 | from pathlib import Path 7 | 8 | import click 9 | 10 | from .. import __file__ as root_module_path 11 | from ..meta import NAME, VERSION, DESCRIPTION, REPO_URL, DOCS_URL, LICENSE, AUTHOR 12 | from ..utils import embolden, italicize 13 | 14 | 15 | @click.command(options_metavar="[options]") 16 | def info(): 17 | """Displays package information.""" 18 | with open(Path(root_module_path).parent / "data" / "logo.txt", "r") as file_stream: 19 | logo_text = file_stream.read().strip() 20 | 21 | output = logo_text.split("\n") 22 | 23 | line = 3 24 | output[line] += f" {embolden(NAME)}" 25 | 26 | line += 2 27 | output[line] += f" {italicize(DESCRIPTION)}" 28 | 29 | line += 3 30 | output[line] += f" {italicize('Version')} : {VERSION}" 31 | 32 | line += 1 33 | output[line] += f" {italicize('License')} : {LICENSE}" 34 | 35 | line += 1 36 | output[line] += f" {italicize('Author')} : {AUTHOR}" 37 | 38 | line += 3 39 | output[line] += f" {italicize('Source')} : {REPO_URL}" 40 | 41 | line += 1 42 | output[line] += f" {italicize('Docs')} : {DOCS_URL}" 43 | 44 | print("\n".join(output[1:-1])) 45 | -------------------------------------------------------------------------------- /src/data/logo.txt: -------------------------------------------------------------------------------- 1 |                                        2 |                                        3 |          @@@@@@@@@@@@@@@@@             4 |       @@@@@@@@@@@@@@@@@@@@@@@@         5 |      @@@@@@@@@?????@@@PPPPP@@@@@@      6 |      @@@@@@@@@?????@@@PPPPPP@@@@@@     7 |       @@@@@@@@@@@@@@@@@PPP@@@@@@@@@    8 |         @@@@@@@@@@@@@@@@@@@@@@@@@@@@   9 |         @@@@@@@@@@@@@@@@PPPPP@@@@@@@   10 |       @@@@@@@@@@@@@@@@@@PPPPP@@@@@@@   11 |      @@@@@@@@@@@@@????@@@PPP@@@@@@@    12 |    @@@@@@????@@@@??????@@@@@@@@@@      13 |   @@@@@@??????@@@@????@@@@@@@@@@       14 |   @@@@@@@????@@@@@@@@@@@@@@@@          15 |    @@@@@@@@@@@@@@@@@@@@@@              16 |     @@@@@@@@@@@@@@@@                   17 |                                        18 |                                        19 | -------------------------------------------------------------------------------- /src/draw/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This package defines drawers for different styles of text art. 3 | 4 | !!! question "Styles" 5 | Refer to the [Styles documentation](../../styles/index.md) 6 | for an in-depth guide to the **image processing behind Picharsso**. 7 | """ 8 | 9 | from .gradient import GradientDrawer 10 | from .base import BaseDrawer, RESAMPLING_FILTERS, DEFAULT_RESAMPLING 11 | from .braille import BrailleDrawer 12 | 13 | DRAWERS: dict = {"gradient": GradientDrawer, "braille": BrailleDrawer} 14 | """The collection of drawers.""" 15 | 16 | 17 | def new_drawer(style, **kwargs): 18 | """Creates a new drawer instance. 19 | 20 | Args: 21 | style (str): The style of the text art. 22 | **kwargs (dict): Appropriate keyword arguments. 23 | See [`BaseDrawer`][picharsso.draw.base.BaseDrawer] 24 | and others. 25 | 26 | Returns: 27 | Type[picharsso.draw.BaseDrawer]: The new drawer instance. 28 | """ 29 | return DRAWERS[style](**kwargs) 30 | -------------------------------------------------------------------------------- /src/draw/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module defines an abstract base drawer. 3 | 4 | !!! question "Styles" 5 | Refer to the [Styles documentation](../../styles/index.md) 6 | for an in-depth guide to the **image processing behind Picharsso**. 7 | """ 8 | 9 | from abc import ABC, abstractmethod 10 | 11 | from PIL import Image 12 | 13 | from ..format import new_formatter 14 | from ..utils import ensure_rgb 15 | 16 | RESAMPLING_FILTERS: dict = { 17 | "nearest": Image.NEAREST, 18 | "box": Image.BOX, 19 | "bilinear": Image.BILINEAR, 20 | "hamming": Image.HAMMING, 21 | "bicubic": Image.BICUBIC, 22 | "lanczos": Image.LANCZOS, 23 | } 24 | """A collection of resampling filters. 25 | See [Pillow's Filters](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#filters){target=_blank}. 26 | """ 27 | 28 | DEFAULT_RESAMPLING: str = "nearest" 29 | """The default resampling filter.""" 30 | 31 | 32 | class BaseDrawer(ABC): 33 | """ 34 | An abstract base drawer. 35 | 36 | Attributes: 37 | height (int): The desired height of the text in characters. 38 | width (int): The desired width of the text in characters. 39 | resample (int): The resampling filter. 40 | format (Type[picharsso.format.BaseFormatter]): The formatter instance. 41 | 42 | Note: 43 | The following methods must be overwritten: 44 | 45 | - [`calculate_size`][picharsso.draw.base.BaseDrawer.calculate_size] 46 | - [`process`][picharsso.draw.base.BaseDrawer.process] 47 | """ 48 | 49 | def __init__(self, height=42, width=0, resample=DEFAULT_RESAMPLING, **kwargs): 50 | """Initialization method. 51 | 52 | Args: 53 | height (Optional[int]): The desired height of the text in characters. 54 | width (Optional[int]): The desired width of the text in characters. 55 | resample (Optional[str]): The resampling filter. 56 | **kwargs (dict): Appropriate keyword arguments. 57 | See [`BaseFormatter`][picharsso.format.base.BaseFormatter] and others. 58 | 59 | Note: 60 | When set as `0`, `height` is derived from `width` and vice versa. 61 | This is done to preserve the aspect ratio of the image. 62 | """ 63 | self.height = None 64 | self.width = None 65 | self.resample = None 66 | BaseDrawer.set(self, height=height, width=width, resample=resample) 67 | 68 | self.format = new_formatter(**kwargs) 69 | 70 | def __call__(self, image): 71 | """Applies processing and formatting on the `image` 72 | and returns a single string. 73 | 74 | Args: 75 | image (PIL.Image.Image): The subject image. 76 | 77 | Returns: 78 | str: The string of text art. 79 | """ 80 | # Ensure that the image is in the `RGB` mode. 81 | image = ensure_rgb(image) 82 | 83 | # Calculate the new size of the image, for processing the text matrix. 84 | image_size = self.calculate_size(image.size[::-1]) 85 | 86 | # Process text matrix from the resized image. 87 | text_matrix = self.process( 88 | image.resize(image_size[::-1], resample=self.resample) 89 | ) 90 | 91 | # Apply formatting. 92 | return self.format(text_matrix, image, self.resample) 93 | 94 | @abstractmethod 95 | def calculate_size(self, image_size): 96 | """Calculates the size of the image for processing the text matrix. 97 | 98 | Args: 99 | image_size (Tuple[int, int]): The height and width of the subject image. 100 | 101 | Returns: 102 | Tuple[int, int]: The size of the image. 103 | """ 104 | 105 | @abstractmethod 106 | def process(self, image): 107 | """Converts an image to a matrix of text. 108 | 109 | Args: 110 | image (PIL.Image.Image): The subject image, 111 | with `mode = "RGB"`, 112 | and `size = (, )`. 113 | 114 | Returns: 115 | numpy.ndarray: The text matrix, 116 | with `shape = (, )`, 117 | and `dtype = str`. 118 | """ 119 | 120 | def set(self, height=None, width=None, resample=None): 121 | """Sets attributes of the drawer instance. 122 | 123 | Args: 124 | height (Option[int]): Sets `height`. 125 | width (Option[int]): Sets `width`. 126 | resample (Option[str]): Sets `resample`. 127 | 128 | Raises: 129 | ValueError: If both `height` and `width` are set to `0`. 130 | """ 131 | # Set resampling filter 132 | if resample is not None: 133 | self.resample = RESAMPLING_FILTERS[resample] 134 | 135 | # Set height and width 136 | if height is not None or width is not None: 137 | new_h = self.height if height is None else height 138 | new_w = self.width if width is None else width 139 | 140 | if new_h == 0 and new_w == 0: 141 | raise ValueError("Either height or width must be non-zero") 142 | 143 | self.height = new_h 144 | self.width = new_w 145 | 146 | 147 | __all__ = ["BaseDrawer", "RESAMPLING_FILTERS", "DEFAULT_RESAMPLING"] 148 | -------------------------------------------------------------------------------- /src/draw/braille.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module defines a drawer for the [Braille style](../../styles/braille.md). 3 | 4 | ??? example "Example" 5 | Consider the following image: 6 | 7 |
8 |

9 | Apple logo 10 |

11 |

12 | Apple Computer [Rob Janoff, 1977] 13 |

14 |
15 | 16 | Here's what it should look like: 17 | 18 |
19 | Apple logo in text (Braille style) 23 |
24 | """ 25 | 26 | import numpy as np 27 | 28 | from .base import BaseDrawer 29 | from ..utils import submatrices 30 | 31 | DEFAULT_THRESHOLD: int = 64 32 | """The default threshold grayscale intensity.""" 33 | 34 | 35 | class BrailleDrawer(BaseDrawer): 36 | """ 37 | A drawer for the [Braille style](../../styles/braille.md). 38 | 39 | Inherits [`BaseDrawer`][picharsso.draw.base.BaseDrawer]. 40 | 41 | Attributes: 42 | threshold (int): Threshold grayscale intensity for pixels to be considered. 43 | kernel (numpy.ndarray): A hard-coded matrix relating the intensity to 44 | the Unicode values for Braille characters. 45 | charset_array (numpy.ndarray): A matrix of all Braille characters, 46 | indexed by their offsetted Unicode value. 47 | """ 48 | 49 | def __init__(self, threshold=DEFAULT_THRESHOLD, **kwargs): 50 | """Initialization method. 51 | 52 | Args: 53 | threshold (Optional[int]): Threshold grayscale intensity 54 | for pixels to be considered. 55 | """ 56 | 57 | super().__init__(**kwargs) 58 | self.threshold = None 59 | self.set(threshold=threshold) 60 | 61 | self.kernel = np.array([[1, 8], [2, 16], [4, 32], [64, 128]]).astype(np.uint8) 62 | self.charset_array = np.array([chr(ord("\u2800") + x) for x in range(256)]) 63 | 64 | def calculate_size(self, image_size): 65 | # Possible dimensions 66 | new_h = self.height 67 | new_w = self.width 68 | 69 | new_h = new_h * 4 70 | new_w = new_w * 2 71 | 72 | # Image dimensions 73 | old_h, old_w = image_size 74 | 75 | # If height is not set, infer it from width 76 | if not new_h: 77 | new_h = int(round(old_h / old_w * new_w)) 78 | 79 | # If width is not set, infer it from height 80 | if not new_w: 81 | new_w = int(round(old_w / old_h * new_h)) 82 | 83 | return new_h, new_w 84 | 85 | def process(self, image): 86 | # Convert the image mode to grayscale. 87 | # Filter all pixels with intensity greater than or equal to the threshold. 88 | # Perform a convolution on this filtered image with the Braille kernel. 89 | # The resultant matrix has the offsetted Unicode values, i.e., indices 90 | # for the corresponding Braille characters that form the image. 91 | # Index the character set with the indices. 92 | return self.charset_array[ 93 | np.einsum( 94 | "ij,klij->kl", 95 | self.kernel, 96 | submatrices( 97 | (np.array(image.convert("L")) >= self.threshold).astype(np.uint8), 98 | self.kernel.shape, 99 | ), 100 | ) 101 | ] 102 | 103 | def set(self, threshold=None, **kwargs): 104 | """Sets attributes of the drawer instance. 105 | 106 | Args: 107 | threshold (Optional[int]): Sets `threshold`. 108 | **kwargs (dict): Appropriate keyword arguments. 109 | See [`BaseDrawer.set`][picharsso.draw.base.BaseDrawer.set]. 110 | """ 111 | super().set(**kwargs) 112 | 113 | if threshold is not None: 114 | self.threshold = threshold 115 | 116 | 117 | __all__ = ["BrailleDrawer"] 118 | -------------------------------------------------------------------------------- /src/draw/gradient.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module defines a drawer for the [gradient style](../../styles/gradient.md). 3 | 4 | ??? example "Example" 5 | Consider the following image: 6 | 7 |
8 |

9 | Apple logo 10 |

11 |

12 | Apple Computer [Rob Janoff, 1977] 13 |

14 |
15 | 16 | Here's what it should look like: 17 | 18 |
19 | Apple logo in text (gradient style) 23 |
24 | """ 25 | 26 | import numpy as np 27 | 28 | from .base import BaseDrawer 29 | 30 | DEFAULT_CHARSET: str = " :!?PG@" 31 | """The default character set.""" 32 | 33 | 34 | class GradientDrawer(BaseDrawer): 35 | """ 36 | A drawer for the [gradient style](../../styles/gradient.md). 37 | 38 | Inherits [`BaseDrawer`][picharsso.draw.base.BaseDrawer]. 39 | 40 | Attributes: 41 | charset (str): A set of characters ordered by the amount of area 42 | their symbols occupy. 43 | negative (bool): Whether or not to reverse the `charset`. 44 | charset_array (numpy.ndarray): A vectorized version of the `charset`. 45 | """ 46 | 47 | def __init__(self, charset=DEFAULT_CHARSET, negative=False, **kwargs): 48 | """Initialization method. 49 | 50 | Args: 51 | charset (Optional[str]): A set of characters ordered 52 | by the amount of area their symbols occupy. 53 | Defaults to `DEFAULT_CHARSET` 54 | negative (Optional[bool]): Whether or not to reverse the `charset`. 55 | **kwargs (dict): Appropriate keyword arguments. 56 | See [`BaseDrawer`][picharsso.draw.base.BaseDrawer]. 57 | """ 58 | super().__init__(**kwargs) 59 | self.charset = None 60 | self.negative = None 61 | self.charset_array = None 62 | self.set(charset=charset, negative=negative) 63 | 64 | def calculate_size(self, image_size): 65 | # Possible dimensions 66 | new_h = self.height 67 | new_w = self.width 68 | 69 | # Image dimensions 70 | old_h, old_w = image_size 71 | 72 | # If height is not set, infer it from width 73 | if not new_h: 74 | new_h = int(round(old_h / old_w * new_w / 2.125)) 75 | 76 | # If width is not set, infer it from height 77 | if not new_w: 78 | new_w = int(round(old_w / old_h * new_h * 2.125)) 79 | 80 | return new_h, new_w 81 | 82 | def process(self, image): 83 | # Convert the image mode to grayscale. 84 | # Normalize the pixel values from a range of (0, 255) to (0, len(self.charset)-1), 85 | # to obtain indices for the character set. 86 | # Index the character set array with the indices. 87 | return self.charset_array[ 88 | np.round( 89 | np.array(image.convert("L")) / 255 * (len(self.charset) - 1) 90 | ).astype(int) 91 | ] 92 | 93 | def set(self, charset=None, negative=None, **kwargs): 94 | """Sets attributes of the drawer instance. 95 | 96 | Args: 97 | charset (Optional[str]): Sets `charset`. 98 | negative (Optional[bool]): Sets `negative`. 99 | **kwargs (dict): Appropriate keyword arguments. 100 | See [`BaseDrawer.set`][picharsso.draw.base.BaseDrawer.set]. 101 | """ 102 | super().set(**kwargs) 103 | 104 | if charset is not None: 105 | self.charset = charset 106 | 107 | if negative is not None: 108 | self.negative = negative 109 | 110 | self.charset_array = np.array( 111 | list(self.charset if not self.negative else self.charset[::-1]) 112 | ) 113 | 114 | 115 | __all__ = ["GradientDrawer"] 116 | -------------------------------------------------------------------------------- /src/format/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This package defines formatters for different modes of text output. 3 | 4 | !!! question "Formats" 5 | Refer to the [Formats documentation](../../formats/index.md) 6 | to learn about the supported output formats. 7 | """ 8 | 9 | from .ansi import AnsiFormatter 10 | from .base import BaseFormatter 11 | from .html import HtmlFormatter 12 | 13 | FORMATTERS: dict = {"ansi": AnsiFormatter, "html": HtmlFormatter} 14 | """The collection of formatters.""" 15 | 16 | DEFAULT_FORMATTER: str = "ansi" 17 | """The default formatter.""" 18 | 19 | 20 | def new_formatter(mode=DEFAULT_FORMATTER, **kwargs): 21 | """Creates a new formatter instance. 22 | 23 | Args: 24 | mode (Option[str]): The mode of the output text. 25 | Defaults to `DEFAULT_FORMATTER`. 26 | **kwargs (dict): Appropriate keyword arguments. 27 | See [`BaseFormatter`][picharsso.format.base.BaseFormatter] 28 | and others. 29 | 30 | Returns: 31 | Type[picharsso.format.BaseFormatter] : The new formatter instance. 32 | """ 33 | return FORMATTERS[mode](**kwargs) 34 | -------------------------------------------------------------------------------- /src/format/ansi.py: -------------------------------------------------------------------------------- 1 | """This module defines a formatter for the [ANSI coloring scheme](../../formats/ansi.md).""" 2 | 3 | from sty import fg 4 | 5 | from .base import BaseFormatter 6 | 7 | 8 | class AnsiFormatter(BaseFormatter): 9 | """ 10 | A formatter for the [ANSI coloring scheme](../../formats/ansi.md). 11 | 12 | Inherits [`BaseFormatter`][picharsso.format.base.BaseFormatter]. 13 | """ 14 | 15 | @staticmethod 16 | def color(text, color): 17 | return f"{fg(*color)}{text}{fg.rs}" 18 | 19 | @staticmethod 20 | def translate(text_matrix): 21 | return text_matrix 22 | 23 | @staticmethod 24 | def unify(text_matrix): 25 | return "\n".join(["".join(row) for row in text_matrix]) 26 | 27 | 28 | __all__ = ["AnsiFormatter"] 29 | -------------------------------------------------------------------------------- /src/format/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module defines an abstract base formatter. 3 | 4 | !!! question "Formats" 5 | Refer to the [Formats documentation](../../formats/index.md) 6 | to learn about the supported output formats. 7 | """ 8 | 9 | from abc import ABC, abstractmethod 10 | 11 | import numpy as np 12 | from numpy.lib.recfunctions import unstructured_to_structured 13 | 14 | 15 | class BaseFormatter(ABC): 16 | """ 17 | An abstract base formatter. 18 | 19 | Attributes: 20 | colorize (bool): Whether to color the text. 21 | vcolor (Callable): The vectorized implementation of the `color` method. 22 | 23 | Note: 24 | The following methods must be overwritten: 25 | 26 | - [`color`][picharsso.format.base.BaseFormatter.color] 27 | - [`translate`][picharsso.format.base.BaseFormatter.translate] 28 | - [`unify`][picharsso.format.base.BaseFormatter.unify] 29 | """ 30 | 31 | def __init__(self, colorize=False): 32 | """Initialization method. 33 | 34 | Args: 35 | colorize (Option[bool]): Whether to color the text. 36 | """ 37 | self.colorize = None 38 | BaseFormatter.set(self, colorize=colorize) 39 | 40 | self.vcolor = np.vectorize(self.color) 41 | 42 | def __call__(self, text_matrix, image, resample): 43 | """Applies formatting and colorization on the `text_matrix` 44 | and returns a single string. 45 | 46 | Args: 47 | text_matrix (numpy.ndarray): The subject text matrix, 48 | with `shape = (, )`, 49 | and `dtype = str`. 50 | image (PIL.Image.Image): The subject image. 51 | resample (int): The resampling filter. 52 | 53 | Returns: 54 | str: The formatted string of text with color (if specified). 55 | """ 56 | 57 | text_size = text_matrix.shape 58 | 59 | # Apply any translations. 60 | text_matrix = self.translate(text_matrix) 61 | 62 | # Colorize if necessary 63 | if self.colorize: 64 | # Pool the colors from the original image by resizing it to the size of the text output. 65 | # Using the vectorized `color` method, color each element in the `text_martix`. 66 | # The vectorized operation takes a `str` from `text_matrix` 67 | # and a `List[int, int, int]` from the pooled colors. 68 | text_matrix = self.vcolor( 69 | text_matrix, 70 | unstructured_to_structured( 71 | np.array(image.resize(text_size[::-1], resample=resample)).astype( 72 | np.uint8 73 | ) 74 | ).astype("O"), 75 | ) 76 | 77 | return self.unify(text_matrix) 78 | 79 | @staticmethod 80 | @abstractmethod 81 | def color(text, color): 82 | """Applies `color` to a string of `text`. 83 | 84 | Args: 85 | text (str): The subject text. 86 | color (Tuple[int, int, int]): The `RGB` value for the color. 87 | 88 | Returns: 89 | str: The colored text. 90 | """ 91 | 92 | @staticmethod 93 | @abstractmethod 94 | def translate(text_matrix): 95 | """Applies translatations to `text_matrix`. 96 | 97 | Args: 98 | text_matrix (numpy.ndarray): The subject text matrix, 99 | with `shape = (, )`, 100 | and `dtype = str`. 101 | 102 | Returns: 103 | numpy.ndarray: The translated text_matrix. 104 | """ 105 | 106 | @staticmethod 107 | @abstractmethod 108 | def unify(text_matrix): 109 | """Formats a `text_matrix` into a single string. 110 | 111 | Args: 112 | text_matrix (numpy.ndarray): The subject text matrix, 113 | with `shape = (, )`, 114 | and `dtype = str`. 115 | 116 | Returns: 117 | str: The formatted string of text art. 118 | """ 119 | 120 | def set(self, colorize=None): 121 | """Sets attributes of the formatter instance. 122 | 123 | Args: 124 | colorize (Optional[bool]): Sets `colorize`. 125 | """ 126 | if colorize is not None: 127 | self.colorize = colorize 128 | 129 | 130 | __all__ = ["BaseFormatter"] 131 | -------------------------------------------------------------------------------- /src/format/html.py: -------------------------------------------------------------------------------- 1 | """This module defines a formatter for [HTML](../../formats/html.md).""" 2 | 3 | from html.entities import name2codepoint 4 | 5 | import numpy as np 6 | 7 | from .base import BaseFormatter 8 | 9 | HTML_ENTITY_MAP: dict = {chr(value): key for (key, value) in name2codepoint.items()} 10 | """A dictionary mapping unicode characters to their equivalent HTML entities.""" 11 | 12 | HTML_ENTITY_MAP[" "] = "nbsp;" 13 | 14 | 15 | class HtmlFormatter(BaseFormatter): 16 | """ 17 | A formatter for [HTML](../../formats/html.md). 18 | 19 | Inherits [`BaseFormatter`][picharsso.format.base.BaseFormatter]. 20 | """ 21 | 22 | @staticmethod 23 | def color(text, color): 24 | return f'{text}' 25 | 26 | @staticmethod 27 | def translate(text_matrix): 28 | unique_chars = np.unique(text_matrix) 29 | 30 | # Change datatype to accomodate strings of varying length 31 | text_matrix = text_matrix.astype( 32 | f"{}
".format( 45 | "\n".join([f"
{''.join(row)}
" for row in text_matrix]) 46 | ) 47 | 48 | 49 | __all__ = ["HtmlFormatter", "HTML_ENTITY_MAP"] 50 | -------------------------------------------------------------------------------- /src/meta.py: -------------------------------------------------------------------------------- 1 | """This module defines variables for the package metadata.""" 2 | 3 | NAME: str = "picharsso" 4 | """The package name.""" 5 | 6 | DESCRIPTION: str = "A utility for converting images to text art." 7 | """The package description.""" 8 | 9 | VERSION: str = "2.0.1" 10 | """The current package version. 11 | """ 12 | 13 | REPO_URL: str = f"https://github.com/kelvindecosta/{NAME}" 14 | """The URL of the package source code.""" 15 | 16 | DOCS_URL: str = f"https://kelvindecosta.github.io/{NAME}" 17 | """The URL of the package documentation.""" 18 | 19 | AUTHOR: str = "Kelvin DeCosta" 20 | """The package author.""" 21 | 22 | LICENSE: str = "MIT" 23 | """The package license.""" 24 | -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | """This module defines utility functions that are used across the package.""" 2 | 3 | from os import system, name 4 | import shutil 5 | 6 | import numpy as np 7 | from PIL import Image 8 | from sty import ef, rs 9 | 10 | 11 | def clear_screen(): 12 | """Clears the terminal console.""" 13 | # Windows systems 14 | if name == "nt": 15 | _ = system("cls") 16 | # Unix systems 17 | else: 18 | _ = system("clear") 19 | 20 | 21 | def embolden(text): 22 | """Modifies text to appear in a bold typeface, 23 | using [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). 24 | 25 | Args: 26 | text (str): The subject text. 27 | 28 | Returns: 29 | str: The text in a bold typeface. 30 | """ 31 | return f"{ef.bold}{text}{rs.bold_dim}" 32 | 33 | 34 | def ensure_rgb(image): 35 | """Usually converts any [Pillow](https://python-pillow.org/) 36 | `image` to its equivalent in the `RGB` mode. 37 | 38 | Args: 39 | image (PIL.Image.Image): The subject image. 40 | 41 | Returns: 42 | PIL.Image.Image: The image in the `RGB` mode. 43 | """ 44 | # If the image has a color palette, 45 | # convert to the `RGBA` mode. 46 | if image.mode == "P": 47 | image = image.convert("RGBA") 48 | 49 | # If the image is in `RGBA` mode, 50 | # create a white background. 51 | if image.mode == "RGBA": 52 | temp = Image.new("RGB", image.size, (255, 255, 255)) 53 | temp.paste(image, mask=image.split()[3]) 54 | image = temp 55 | 56 | # Convert to `RGB` mode 57 | return image.convert("RGB") 58 | 59 | 60 | def italicize(text): 61 | """Modifies text to appear in italics, 62 | using [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). 63 | 64 | Args: 65 | text (str): The subject text. 66 | 67 | Returns: 68 | str: The text in italics. 69 | """ 70 | return f"{ef.italic}{text}{rs.italic}" 71 | 72 | 73 | def submatrices(matrix, shape): 74 | """Returns a rolling window view of a `matrix`, without overlapping, 75 | given the `shape` of the window. 76 | 77 | Args: 78 | matrix (numpy.ndarray): The subject matrix. 79 | shape (Tuple[int, int]): The `` and `` of the window. 80 | 81 | Returns: 82 | numpy.ndarray: The rolling window view of the matrix. 83 | 84 | Note: 85 | This operation doesn't account for the loss of border elements. 86 | """ 87 | # Extract strides and shapes for calculation. 88 | mat_hs, mat_ws = matrix.strides[:2] 89 | mat_h, mat_w = matrix.shape[:2] 90 | ker_h, ker_w = shape 91 | 92 | # View `matrix` according to new strides and shape. 93 | return np.lib.stride_tricks.as_strided( 94 | matrix, 95 | (1 + (mat_h - ker_h) // ker_h, 1 + (mat_w - ker_w) // ker_w, ker_h, ker_w) 96 | + matrix.shape[2:], 97 | strides=(ker_h * mat_hs, ker_w * mat_ws, mat_hs, mat_ws) + matrix.strides[2:], 98 | ) 99 | 100 | 101 | def terminal_size(): 102 | """Returns the size of the terminal window. 103 | 104 | Returns: 105 | (Tuple[int, int]): The `` and `` 106 | of the terminal window in characters. 107 | 108 | Note: 109 | When used while piping, 110 | this function usually returns the default terminal size, `(24, 80)`. 111 | """ 112 | return shutil.get_terminal_size()[::-1] 113 | 114 | 115 | __all__ = [ 116 | "clear_screen", 117 | "embolden", 118 | "ensure_rgb", 119 | "italicize", 120 | "submatrices", 121 | "terminal_size", 122 | ] 123 | --------------------------------------------------------------------------------