Set default image ratio to terminal height, included --map flag, improved help text

This commit is contained in:
Zoraiz 2021-05-27 14:42:50 +05:00
parent f022070c20
commit b09a5540d9
7 changed files with 155 additions and 92 deletions

View File

@ -28,7 +28,7 @@ archives:
format: zip format: zip
files: files:
- LICENSE - LICENSE.txt
replacements: replacements:
linux: Linux linux: Linux

123
README.md
View File

@ -1,38 +1,36 @@
# ascii-image-converter # ascii-image-converter
ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. It is cross-platform so both Windows and Linux distributions are supported ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. It is cross-platform so both Windows and Linux distributions are supported.
Image formats currently supported: Image formats currently supported:
* JPEG/JPG * JPEG/JPG
* PNG * PNG
* WEBP
* BMP * BMP
* WEBP
* TIFF/TIF * TIFF/TIF
## Table of Contents ## Table of Contents
- [Example](#example-source)
- [Installation](#installation)
* [Snap](#snap)
* [Go](#go)
* [Linux (binaries)](#linux)
* [Windows (binaries)](#windows)
- [Usage](#usage)
* [Flags](#flags)
- [Contributing](#contributing)
- [Packages used](#packages-used)
- [License](#license)
- [Example](#example-source)
- [Installation](#installation)
* [Snap](#snap)
* [Go](#go)
* [Linux (binaries)](#linux)
* [Windows (binaries)](#windows)
- [Usage](#usage)
* [Flags](#flags)
- [Contributing](#contributing)
- [Packages used](#packages-used)
- [License](#license)
### Example ([Source](https://medium.com/@sean.glancy/practical-applications-of-binary-trees-3097cf663062)): ### Example ([Source](https://medium.com/@sean.glancy/practical-applications-of-binary-trees-3097cf663062)):
![Example](https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_images/tree.png) ![Example](https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_images/tree.png)
### ASCII Art: ### ASCII Art:
![Example](https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_images/ascii_tree.png) ![Example](https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_images/ascii_tree.png)
## Installation ## Installation
### Snap ### Snap
@ -40,12 +38,14 @@ Image formats currently supported:
You can download through snap. However, the snap will not have access to hidden images and images outside the $HOME directory. You can download through snap. However, the snap will not have access to hidden images and images outside the $HOME directory.
``` ```
sudo snap install ascii-image-converter --stable sudo snap install ascii-image-converter
``` ```
Visit [the app's snap store listing](https://snapcraft.io/ascii-image-converter) for instructions regarding enabling snapd on your distribution. Visit [the app's snap store listing](https://snapcraft.io/ascii-image-converter) for instructions regarding enabling snapd on your distribution.
<hr> <hr>
### Go ### Go
For installing through Go For installing through Go
``` ```
go install github.com/TheZoraiz/ascii-image-converter@latest go install github.com/TheZoraiz/ascii-image-converter@latest
@ -54,7 +54,8 @@ go install github.com/TheZoraiz/ascii-image-converter@latest
For physically installing the binaries, follow the steps with respect to your OS. For physically installing the binaries, follow the steps with respect to your OS.
### Linux ### Linux
Extract the archive for your chosen Linux architecture after downloading it from [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest), and open the extracted directory.
Download the archive for your distribution's architecture [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest), extract it, and open the extracted directory.
Now, open a terminal in the same directory and execute this command: Now, open a terminal in the same directory and execute this command:
@ -67,7 +68,7 @@ Now you can use ascii-image-converter in the terminal. Execute "ascii-image-conv
You will need to set an Environment Variable to the folder the ascii-image-converter.exe executable is placed in to be able to use it in the command prompt. Follow the instructions in case of confusion: You will need to set an Environment Variable to the folder the ascii-image-converter.exe executable is placed in to be able to use it in the command prompt. Follow the instructions in case of confusion:
Extract the archive for the your chosen Windows architecture after downloading it from [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest). Open the newly created folder and copy the path to it from the top of the file explorer. Download the archive for your Windows architecture [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest), extract it, and open the extracted folder.
* In Search, search for and then select: System (Control Panel) * In Search, search for and then select: System (Control Panel)
* Click the Advanced System settings link. * Click the Advanced System settings link.
* Click Environment Variables. In the section User Variables find the Path environment variable and select it. Click "Edit". * Click Environment Variables. In the section User Variables find the Path environment variable and select it. Click "Edit".
@ -82,77 +83,105 @@ Now, restart any open command prompt and execute "ascii-image-converter -h" for
Note: Decrease font size or increase terminal width (like zooming out) for maximum quality ascii art Note: Decrease font size or increase terminal width (like zooming out) for maximum quality ascii art
To convert an image into ascii format, the usage is as follows: The basic usage for converting an image into ascii art is as follows. You can also supply paths to multiple images.
``` ```
ascii-image-converter [path to image] ascii-image-converter [image-paths]
``` ```
Example Example:
``` ```
ascii-image-converter myImage.jpeg ascii-image-converter myImage.jpeg
``` ```
### Flags ### Flags
#### --complex OR -c #### --color OR -C
Print the image with a wider array of ascii characters. Sometimes improves accuracy.
Display ascii art with the colors from original image. Works with the --negative flag as well.
``` ```
ascii-image-converter [path to image] -c ascii-image-converter [image-paths] -C
# Or # Or
ascii-image-converter [path to image] --complex ascii-image-converter [image-paths] --color
``` ```
#### --complex OR -c
Print the image with a wider array of ascii characters for more detailed lighting density. Sometimes improves accuracy.
```
ascii-image-converter [image-paths] -c
# Or
ascii-image-converter [image-paths] --complex
```
#### --dimensions OR -d #### --dimensions OR -d
Set the width and height for ascii art in CHARACTER lengths. (Don't immediately append another flag with -d)
Note: Don't immediately append another flag with -d
Set the width and height for ascii art in CHARACTER lengths.
``` ```
ascii-image-converter [path to image] -d <width>,<height> ascii-image-converter [image-paths] -d <width>,<height>
# Or # Or
ascii-image-converter [path to image] --dimensions <width>,<height> ascii-image-converter [image-paths] --dimensions <width>,<height>
``` ```
Example: Example:
``` ```
ascii-image-converter [path to image] -d 100,30 ascii-image-converter [image-paths] -d 100,30
``` ```
#### --color OR -C #### --map OR -m
Display ascii art with the colors from original image. Works with the -n flag as well.
Note: Don't immediately append another flag with -m
Pass a string of your own ascii characters to map against. Passed characters must start from darkest character and end with lightest. There is no limit to number of characters.
Notes: Empty spaces can be passed if string is passed inside quotation marks. You can use both single or double quote for quotation marks. For repeating quotation mark inside string, append it with \ (such as \\").
``` ```
ascii-image-converter [path to image] -C ascii-image-converter [image-paths] -m "<string-of-characters>"
# Or # Or
ascii-image-converter [path to image] --color ascii-image-converter [image-paths] --map "<string-of-characters>"
```
Following example contains 7 depths of lighting.
```
ascii-image-converter [image-paths] -m " .-=+#@"
``` ```
#### --negative OR -n #### --negative OR -n
Display ascii art in negative colors. Works with both uncolored and colored text from -C flag.
Display ascii art in negative colors. Works with both uncolored and colored text from --color flag.
``` ```
ascii-image-converter [path to image] -n ascii-image-converter [image-paths] -n
# Or # Or
ascii-image-converter [path to image] --negative ascii-image-converter [image-paths] --negative
``` ```
#### --save OR -s #### --save OR -s
Save the printed ascii art in a file ascii-image.txt in the directory passed alongside. (Don't immediately append another flag with -s)
Save ascii art in the format `<image-name>-<image-extension>-ascii-art.txt` in the directory path passed to the flag.
Example for current directory: Example for current directory:
``` ```
ascii-image-converter [path to image] --save ./ ascii-image-converter [image-paths] --save .
# Or # Or
ascii-image-converter [path to image] -s ./ ascii-image-converter [image-paths] -s .
``` ```
#### --formats OR -f #### --formats OR -f
Display supported image formats. Display supported image formats.
``` ```
ascii-image-converter [path to image] --formats ascii-image-converter [image-paths] --formats
# Or # Or
ascii-image-converter [path to image] -f ascii-image-converter [image-paths] -f
``` ```
<br> <br>
You can combine flags as well. Following command outputs colored and negative ascii art, with fixed 100 by 30 character dimensions, custom defined ascii characters " .-=+#@" and saves the output in current directory as well.
You can combine flags as well. Following command outputs colored and negative ascii art, with complex characters, fixed 100 by 30 character dimensions and saves the output in current directory as well.
``` ```
ascii-image-converter [path to image] -Ccnd 100,30 -s ./ ascii-image-converter [image-paths] -Cnd 100,30 -m " .-=+#@" -s ./
``` ```
<br> <br>
@ -175,6 +204,6 @@ You can fork the project and implement any changes you want for a pull request.
[github.com/gookit/color](https://github.com/gookit/color) [github.com/gookit/color](https://github.com/gookit/color)
## License ## License
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE)
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE)

View File

@ -49,15 +49,14 @@ var (
negative bool negative bool
formatsTrue bool formatsTrue bool
colored bool colored bool
customMap string
// Root commands // Root commands
rootCmd = &cobra.Command{ rootCmd = &cobra.Command{
Use: "ascii-image-converter [image path]", Use: "ascii-image-converter [image-paths]",
Short: "Converts images into ascii format", Short: "Converts images into ascii format",
Version: "1.2.3", Version: "1.2.4",
Example: " ascii-image-converter myImage.jpeg\n\n" + Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.",
"For further details, visit https://github.com/TheZoraiz/ascii-image-converter",
Long: `This tool converts images into ascii format and prints them onto the terminal window. Further configuration can be managed with flags`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if formatsTrue { if formatsTrue {
@ -65,18 +64,26 @@ var (
return nil return nil
} }
if len(args) != 1 {
return fmt.Errorf("Requires 1 image path, got %v", len(args))
}
numberOfDimensions := len(dimensions) numberOfDimensions := len(dimensions)
if dimensions != nil && numberOfDimensions != 2 { if dimensions != nil && numberOfDimensions != 2 {
return fmt.Errorf("-d requires 2 dimensions, got %v", numberOfDimensions) return fmt.Errorf("-d requires 2 dimensions, got %v", numberOfDimensions)
} }
imagePath := args[0] if len(args) < 1 {
return fmt.Errorf("Need at least 1 image path")
}
return convertImage(imagePath) if len(customMap) < 2 && customMap != "" {
fmt.Println("Need at least 2 characters")
os.Exit(0)
}
for _, imagePath := range args {
if err := convertImage(imagePath); err != nil {
return err
}
}
return nil
}, },
} }
) )
@ -93,22 +100,25 @@ func convertImage(imagePath string) error {
pic, err := os.Open(imagePath) pic, err := os.Open(imagePath)
if err != nil { if err != nil {
return fmt.Errorf("Unable to open file: %w", err) fmt.Printf("Unable to open file: %v\n", err)
os.Exit(0)
} }
defer pic.Close() defer pic.Close()
imData, _, err := image.Decode(pic) imData, _, err := image.Decode(pic)
if err != nil { if err != nil {
return fmt.Errorf("Unable to decode file: %w", err) fmt.Printf("Error decoding file: %v\n", err)
os.Exit(0)
} }
imgSet, err := imgMani.ConvertToAsciiPixels(imData, dimensions) imgSet, err := imgMani.ConvertToAsciiPixels(imData, dimensions)
if err != nil { if err != nil {
return err fmt.Printf("Error: %v\n", err)
os.Exit(0)
} }
var asciiSet [][]imgMani.AsciiChar var asciiSet [][]imgMani.AsciiChar
asciiSet = imgMani.ConvertToAscii(imgSet, negative, colored, compl) asciiSet = imgMani.ConvertToAscii(imgSet, negative, colored, compl, customMap)
var ascii []string var ascii []string
ascii = flattenAscii(asciiSet, colored) ascii = flattenAscii(asciiSet, colored)
@ -116,7 +126,8 @@ func convertImage(imagePath string) error {
// Save art before printing it, if flag is passed // Save art before printing it, if flag is passed
if savePath != "" { if savePath != "" {
if err := saveAsciiArt(asciiSet, imagePath); err != nil { if err := saveAsciiArt(asciiSet, imagePath); err != nil {
return err fmt.Printf("Error: %v\n", err)
os.Exit(0)
} }
} }
@ -153,7 +164,7 @@ func saveAsciiArt(asciiSet [][]imgMani.AsciiChar, imagePath string) error {
if _, err := os.Stat(savePath); !os.IsNotExist(err) { if _, err := os.Stat(savePath); !os.IsNotExist(err) {
return ioutil.WriteFile(savePath+saveFileName, []byte(strings.Join(saveAscii, "\n")), 0777) return ioutil.WriteFile(savePath+saveFileName, []byte(strings.Join(saveAscii, "\n")), 0777)
} else { } else {
return fmt.Errorf("Path does not exist.") return fmt.Errorf("Save path does not exist.")
} }
} }
@ -201,13 +212,19 @@ func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)") // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)")
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with the colors from original image (Can work with the -n flag)") rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with the colors from original image\n(Can work with the -n flag)\n")
rootCmd.PersistentFlags().BoolVarP(&compl, "complex", "c", false, "Display ascii characters in a larger range, may result in higher quality") rootCmd.PersistentFlags().BoolVarP(&compl, "complex", "c", false, "Display ascii characters in a larger range\nMay result in higher quality\n")
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length e.g. 100,30 (defaults to terminal size)") rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 100,30 (defaults to terminal height)\n")
rootCmd.PersistentFlags().BoolVarP(&formatsTrue, "formats", "f", false, "Display supported image formats") rootCmd.PersistentFlags().BoolVarP(&formatsTrue, "formats", "f", false, "Display supported image formats\n")
rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors (Can work with the -C flag)") rootCmd.PersistentFlags().StringVarP(&customMap, "map", "m", "", "Give custom ascii characters to map against\nOrdered from darkest to lightest\ne.g. -m \" .-+#@\" (Quotation marks excluded from map)\n(Cancels --complex flag)\n")
rootCmd.PersistentFlags().StringVarP(&savePath, "save", "s", "", "Save ascii art in an <image-name>-<image-extension>-ascii-art.txt file in a given path (pass . for current directory)") rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors\n(Can work with the --color flag)\n")
rootCmd.PersistentFlags().StringVarP(&savePath, "save", "s", "", "Save ascii art in the format:\n<image-name>-<image-extension>-ascii-art.txt\nFile will be saved in passed path\n(pass . for current directory)\n")
defaultUsageTemplate := rootCmd.UsageTemplate()
rootCmd.SetUsageTemplate("\nCopyright © 2021 Zoraiz Hassan <hzoraiz8@gmail.com>\n" +
"Distributed under the Apache License Version 2.0 (Apache-2.0)\n" +
"For further details, visit https://github.com/TheZoraiz/ascii-image-converter\n\n" +
defaultUsageTemplate)
} }
// initConfig reads in config file and ENV variables if set. // initConfig reads in config file and ENV variables if set.

View File

@ -123,16 +123,24 @@ type AsciiChar struct {
// //
// If complex parameter is true, values are compared to 69 levels of color density in ASCII characters. // If complex parameter is true, values are compared to 69 levels of color density in ASCII characters.
// Otherwise, values are compared to 10 levels of color density in ASCII characters. // Otherwise, values are compared to 10 levels of color density in ASCII characters.
func ConvertToAscii(imgSet [][]AsciiPixel, negative bool, colored bool, complex bool) [][]AsciiChar { func ConvertToAscii(imgSet [][]AsciiPixel, negative bool, colored bool, complex bool, customMap string) [][]AsciiChar {
height := len(imgSet) height := len(imgSet)
width := len(imgSet[0]) width := len(imgSet[0])
var chosenTable map[int]string var chosenTable map[int]string
if complex {
chosenTable = asciiTableDetailed if customMap == "" {
if complex {
chosenTable = asciiTableDetailed
} else {
chosenTable = asciiTableSimple
}
} else { } else {
chosenTable = asciiTableSimple chosenTable = map[int]string{}
for index, char := range customMap {
chosenTable[index] = string(char)
}
} }
result := make([][]AsciiChar, height) result := make([][]AsciiChar, height)

View File

@ -38,31 +38,40 @@ type AsciiPixel struct {
// ranges from 0 to 65535, while RGB values are separate. // ranges from 0 to 65535, while RGB values are separate.
func ConvertToAsciiPixels(img image.Image, dimensions []int) ([][]AsciiPixel, error) { func ConvertToAsciiPixels(img image.Image, dimensions []int) ([][]AsciiPixel, error) {
var terminalWidth, terminalHeight int var asciiWidth, asciiHeight int
var smallImg image.Image var smallImg image.Image
// Get dimensions of current terminal
if len(dimensions) == 0 { if len(dimensions) == 0 {
terminalWidth, _ = consolesize.GetConsoleSize() // Following code in this condition calculates ratio according to terminal height
// Sometimes full length outputs print empty lines between ascii art terminalWidth, terminalHeight := consolesize.GetConsoleSize()
terminalWidth -= 1 asciiHeight = terminalHeight - 1
// Passing 0 in place of height keeps the original image's aspect ratio // Passing 0 in place of width keeps the original image's aspect ratio
smallImg = resize.Resize(uint(terminalWidth), 0, img, resize.Lanczos3) smallImg = resize.Resize(0, uint(asciiHeight), img, resize.Lanczos3)
terminalHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
// To fix height ratio in eventual ascii art // To fix aspect ratio in eventual ascii art
terminalHeight = int(0.5 * float32(terminalHeight)) asciiWidth = int(2 * float32(asciiWidth))
smallImg = resize.Resize(uint(terminalWidth), uint(terminalHeight), img, resize.Lanczos3) // If ascii width exceeds terminal width, change ratio with respect to terminal width
if asciiWidth > terminalWidth {
smallImg = resize.Resize(uint(terminalWidth), 0, img, resize.Lanczos3)
asciiWidth = terminalWidth
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
// To fix aspect ratio in eventual ascii art
asciiHeight = int(0.5 * float32(asciiHeight))
}
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
} else { } else {
terminalWidth = dimensions[0] asciiWidth = dimensions[0]
terminalHeight = dimensions[1] asciiHeight = dimensions[1]
smallImg = resize.Resize(uint(terminalWidth), uint(terminalHeight), img, resize.Lanczos3) smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
} }
// If there are passed dimensions, check whether the width exceeds terminal width // If there are passed dimensions, check whether the width exceeds terminal width
@ -75,9 +84,9 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int) ([][]AsciiPixel, er
} }
// Initialize imgSet 2D slice // Initialize imgSet 2D slice
imgSet := make([][]AsciiPixel, terminalHeight) imgSet := make([][]AsciiPixel, asciiHeight)
for i := range imgSet { for i := range imgSet {
imgSet[i] = make([]AsciiPixel, terminalWidth) imgSet[i] = make([]AsciiPixel, asciiWidth)
} }
b := smallImg.Bounds() b := smallImg.Bounds()

View File

@ -1,6 +1,6 @@
name: ascii-image-converter name: ascii-image-converter
base: core18 base: core18
version: "1.2.3" version: "1.2.4"
summary: Converts images into ascii format summary: Converts images into ascii format
description: | description: |
This tool converts images into ascii format and prints them onto the terminal window. This tool converts images into ascii format and prints them onto the terminal window.