From b09a5540d9993b84dd50870c0a5a665412a89803 Mon Sep 17 00:00:00 2001 From: Zoraiz Date: Thu, 27 May 2021 14:42:50 +0500 Subject: [PATCH] Set default image ratio to terminal height, included --map flag, improved help text --- .goreleaser.yml | 2 +- LICENSE => LICENSE.txt | 0 README.md | 123 +++++++++++++++--------- cmd/root.go | 63 +++++++----- image_manipulation/ascii_conversions.go | 16 ++- image_manipulation/image_conversions.go | 41 +++++--- snapcraft.yaml | 2 +- 7 files changed, 155 insertions(+), 92 deletions(-) rename LICENSE => LICENSE.txt (100%) diff --git a/.goreleaser.yml b/.goreleaser.yml index 3da5a9a..22a7431 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -28,7 +28,7 @@ archives: format: zip files: - - LICENSE + - LICENSE.txt replacements: linux: Linux diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/README.md b/README.md index d9bc723..b4c2df0 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,36 @@ - # 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: * JPEG/JPG * PNG -* WEBP * BMP +* WEBP * TIFF/TIF ## 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](https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_images/tree.png) ### ASCII Art: + ![Example](https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_images/ascii_tree.png) - - - ## Installation ### 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. ``` -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. +
### Go + For installing through Go ``` 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. ### 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: @@ -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: -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) * Click the Advanced System settings link. * 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 -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 ``` ### Flags -#### --complex OR -c -Print the image with a wider array of ascii characters. Sometimes improves accuracy. +#### --color OR -C + +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 -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 -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 , +ascii-image-converter [image-paths] -d , # Or -ascii-image-converter [path to image] --dimensions , +ascii-image-converter [image-paths] --dimensions , ``` Example: ``` -ascii-image-converter [path to image] -d 100,30 +ascii-image-converter [image-paths] -d 100,30 ``` -#### --color OR -C -Display ascii art with the colors from original image. Works with the -n flag as well. +#### --map OR -m + +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 "" # Or -ascii-image-converter [path to image] --color +ascii-image-converter [image-paths] --map "" +``` +Following example contains 7 depths of lighting. +``` +ascii-image-converter [image-paths] -m " .-=+#@" ``` #### --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 -ascii-image-converter [path to image] --negative +ascii-image-converter [image-paths] --negative ``` #### --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 `--ascii-art.txt` in the directory path passed to the flag. Example for current directory: + ``` -ascii-image-converter [path to image] --save ./ +ascii-image-converter [image-paths] --save . # Or -ascii-image-converter [path to image] -s ./ +ascii-image-converter [image-paths] -s . ``` #### --formats OR -f + Display supported image formats. + ``` -ascii-image-converter [path to image] --formats +ascii-image-converter [image-paths] --formats # Or -ascii-image-converter [path to image] -f +ascii-image-converter [image-paths] -f ```
+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 ./ ```
@@ -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) - ## 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) \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index 1fcc8b6..915ad01 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -49,15 +49,14 @@ var ( negative bool formatsTrue bool colored bool + customMap string // Root commands rootCmd = &cobra.Command{ - Use: "ascii-image-converter [image path]", + Use: "ascii-image-converter [image-paths]", Short: "Converts images into ascii format", - Version: "1.2.3", - Example: " ascii-image-converter myImage.jpeg\n\n" + - "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`, + Version: "1.2.4", + Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.", RunE: func(cmd *cobra.Command, args []string) error { if formatsTrue { @@ -65,18 +64,26 @@ var ( return nil } - if len(args) != 1 { - return fmt.Errorf("Requires 1 image path, got %v", len(args)) - } - numberOfDimensions := len(dimensions) if dimensions != nil && numberOfDimensions != 2 { 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) 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() imData, _, err := image.Decode(pic) 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) if err != nil { - return err + fmt.Printf("Error: %v\n", err) + os.Exit(0) } var asciiSet [][]imgMani.AsciiChar - asciiSet = imgMani.ConvertToAscii(imgSet, negative, colored, compl) + asciiSet = imgMani.ConvertToAscii(imgSet, negative, colored, compl, customMap) var ascii []string ascii = flattenAscii(asciiSet, colored) @@ -116,7 +126,8 @@ func convertImage(imagePath string) error { // Save art before printing it, if flag is passed if savePath != "" { 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) { return ioutil.WriteFile(savePath+saveFileName, []byte(strings.Join(saveAscii, "\n")), 0777) } 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) // 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(&compl, "complex", "c", false, "Display ascii characters in a larger range, may result in higher quality") - 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().BoolVarP(&formatsTrue, "formats", "f", false, "Display supported image formats") - rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors (Can work with the -C flag)") - rootCmd.PersistentFlags().StringVarP(&savePath, "save", "s", "", "Save ascii art in an --ascii-art.txt file in a given path (pass . for current directory)") + 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\nMay result in higher quality\n") + 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\n") + 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().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--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 \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. diff --git a/image_manipulation/ascii_conversions.go b/image_manipulation/ascii_conversions.go index fb9384f..82fb85d 100644 --- a/image_manipulation/ascii_conversions.go +++ b/image_manipulation/ascii_conversions.go @@ -123,16 +123,24 @@ type AsciiChar struct { // // 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. -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) width := len(imgSet[0]) var chosenTable map[int]string - if complex { - chosenTable = asciiTableDetailed + + if customMap == "" { + if complex { + chosenTable = asciiTableDetailed + } else { + chosenTable = asciiTableSimple + } } else { - chosenTable = asciiTableSimple + chosenTable = map[int]string{} + for index, char := range customMap { + chosenTable[index] = string(char) + } } result := make([][]AsciiChar, height) diff --git a/image_manipulation/image_conversions.go b/image_manipulation/image_conversions.go index b9c1e97..630e5ab 100644 --- a/image_manipulation/image_conversions.go +++ b/image_manipulation/image_conversions.go @@ -38,31 +38,40 @@ type AsciiPixel struct { // ranges from 0 to 65535, while RGB values are separate. func ConvertToAsciiPixels(img image.Image, dimensions []int) ([][]AsciiPixel, error) { - var terminalWidth, terminalHeight int + var asciiWidth, asciiHeight int var smallImg image.Image - // Get dimensions of current terminal 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 -= 1 + terminalWidth, terminalHeight := consolesize.GetConsoleSize() + asciiHeight = terminalHeight - 1 - // Passing 0 in place of height keeps the original image's aspect ratio - smallImg = resize.Resize(uint(terminalWidth), 0, img, resize.Lanczos3) - terminalHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y + // Passing 0 in place of width keeps the original image's aspect ratio + smallImg = resize.Resize(0, uint(asciiHeight), img, resize.Lanczos3) + asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X - // To fix height ratio in eventual ascii art - terminalHeight = int(0.5 * float32(terminalHeight)) + // To fix aspect ratio in eventual ascii art + 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 { - terminalWidth = dimensions[0] - terminalHeight = dimensions[1] - smallImg = resize.Resize(uint(terminalWidth), uint(terminalHeight), img, resize.Lanczos3) + asciiWidth = dimensions[0] + asciiHeight = dimensions[1] + smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3) } // 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 - imgSet := make([][]AsciiPixel, terminalHeight) + imgSet := make([][]AsciiPixel, asciiHeight) for i := range imgSet { - imgSet[i] = make([]AsciiPixel, terminalWidth) + imgSet[i] = make([]AsciiPixel, asciiWidth) } b := smallImg.Bounds() diff --git a/snapcraft.yaml b/snapcraft.yaml index 57711a4..0029930 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,6 +1,6 @@ name: ascii-image-converter base: core18 -version: "1.2.3" +version: "1.2.4" summary: Converts images into ascii format description: | This tool converts images into ascii format and prints them onto the terminal window.