louis (empty) → 0.1.0.0
raw patch · 5 files changed
+241/−0 lines, 5 filesdep +JuicyPixelsdep +basedep +bytestringsetup-changed
Dependencies added: JuicyPixels, base, bytestring, text, vector
Files
- CHANGELOG.md +6/−0
- LICENSE +20/−0
- Setup.hs +2/−0
- louis.cabal +71/−0
- src/Louis.hs +142/−0
+ CHANGELOG.md view
@@ -0,0 +1,6 @@+# Revision history for louis++## 0.1.0.0 -- 2019-09-22++* First version.+* You can braillize files, ByteStrings-s, and DynamicImage-s of JuicyPixels.
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2019 Alexey Kutepov++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ louis.cabal view
@@ -0,0 +1,71 @@+cabal-version: 2.4++-- Initial package description 'louis.cabal' generated by 'cabal init'.+-- For further documentation, see http://haskell.org/cabal/users-guide/++-- The name of the package.+name: louis++-- The package version. See the Haskell package versioning policy (PVP)+-- for standards guiding when and how versions should be incremented.+-- https://pvp.haskell.org+-- PVP summary: +-+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.1.0.0++-- A short (one-line) description of the package.+synopsis: Turning images into text using Braille font++-- A longer description of the package.+-- description:++-- A URL where users can report bugs.+-- bug-reports:++-- The license under which the package is released.+license: MIT++-- The file containing the license text.+license-file: LICENSE++-- The package author(s).+author: rexim++-- An email address to which users can send suggestions, bug reports, and+-- patches.+maintainer: reximkut@gmail.com++-- A copyright notice.+-- copyright:++category: Graphics++-- Extra files to be distributed with the package, such as examples or a+-- README.+extra-source-files: CHANGELOG.md+++library+ -- Modules exported by the library.+ exposed-modules: Louis++ -- Modules included in this library but not exported.+ -- other-modules:++ -- LANGUAGE extensions used by modules in this package.+ -- other-extensions:++ -- Other library packages from which modules are imported.+ build-depends: base >= 4.8 && < 6+ , JuicyPixels >= 3.3 && < 3.4+ , vector >= 0.10 && < 0.13+ , text >= 1.2 && < 1.3+ , bytestring >=0.9 && <0.11++ -- Directories containing source files.+ hs-source-dirs: src++ -- Base language which the package is written in.+ default-language: Haskell2010+
+ src/Louis.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module : Louis+-- Copyright : (c) Alexey Kutepov 2019+-- License : MIT+-- Maintainer : tsodingbiz@gmail.com+-- Portability : portable+--+-- >>> import Louis+-- >>> import qualified Data.Text as T+-- >>> putStrLn . T.unpack . T.unlines =<< braillizeFile "image.png"+-- ⠀⠀⠀⡸⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿+-- ⠀⢀⣴⣶⣶⣶⣶⣶⣶⣦⣬⣉⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿+-- ⠀⣸⣿⣿⣿⣿⣿⣿⣿⡿⢿⣿⡆⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠿⢿⣿⣿⣿⣿+-- ⠀⣿⣿⣿⣿⣿⣿⣿⣿⠁⢠⣿⠡⠿⠿⠿⠿⣿⣿⣿⢃⣶⣾⣿⣷⣶⣶⣤⣍⡛+-- ⡀⢻⣿⣿⣿⣿⣿⣿⣿⣤⣾⠋⠐⠲⠶⣦⣤⣌⡙⠋⢸⣿⠋⠙⣿⣿⣿⣿⣿⣿+-- ⣿⣦⣭⣉⣉⣙⣛⣋⣉⣉⣅⣾⣿⣿⣷⣾⣿⣿⣿⡇⣿⡏⠀⢰⣿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⢹⣿⣾⣿⣿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣬⡙⠛⢿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⡝⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣬⣍⣛⠻⠿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣶⣶⣶⣦⡉⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣯⣾⣿⣿⣿⣿⣇⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿+-- ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿++module Louis+ ( braillizeDynamicImage+ , braillizeByteString+ , braillizeFile+ ) where++import Data.Word+import Data.Char+import Data.Bits+import Codec.Picture+import qualified Data.Vector.Storable as V+import Data.List+import qualified Data.Text as T+import Data.Functor.Compose+import qualified Data.ByteString as BS++type Chunk = Word8++renderChunk :: Chunk -> Char+renderChunk x = chr (bgroup * groupSize + boffset + ord '⠀')+ where+ bgroup =+ let b1 = (x .&. 0b00001000) `shiftR` 3+ b2 = (x .&. 0b10000000) `shiftR` 6+ in fromIntegral (b1 .|. b2)+ boffset =+ let b1 = (x .&. 0b00000111)+ b2 = (x .&. 0b01110000) `shiftR` 1+ in fromIntegral (b1 .|. b2)+ groupSize = 64++chunkifyGreyScale :: Image Pixel8 -> [[Chunk]]+chunkifyGreyScale img =+ [ [chunkAt (i * 2, j * 4) | i <- [0 .. chunksWidth - 1]]+ | j <- [0 .. chunksHeight - 1]+ ]+ where+ width = imageWidth img+ height = imageHeight img+ chunksWidth = width `div` 2+ chunksHeight = height `div` 4+ squashBits :: [Word8] -> Word8+ squashBits = foldl' (\acc x -> shiftL acc 1 .|. x) 0+ threshold =+ let imgData = imageData img+ in round $+ (/ (fromIntegral $ V.length imgData)) $+ V.foldl' (+) (0.0 :: Float) $ V.map fromIntegral imgData+ k :: Pixel8 -> Word8+ k x+ | x < threshold = 0+ | otherwise = 1+ f :: (Int, Int) -> Word8+ f (x, y)+ | 0 <= x && x < width && 0 <= y && y < height = k $ pixelAt img x y+ | otherwise = 0+ chunkAt :: (Int, Int) -> Chunk+ chunkAt (x, y) =+ squashBits $ reverse [f (i + x, j + y) | i <- [0, 1], j <- [0 .. 3]]++greyScaleImage :: DynamicImage -> Image Pixel8+greyScaleImage = pixelMap greyScalePixel . convertRGBA8+ -- reference: https://www.mathworks.com/help/matlab/ref/rgb2gray.html+ where+ greyScalePixel :: PixelRGBA8 -> Pixel8+ greyScalePixel (PixelRGBA8 r g b a) = k+ where+ k = round ((r' * 0.299 + g' * 0.587 + b' * 0.114) * a')+ r' = fromIntegral r :: Float+ g' = fromIntegral g :: Float+ b' = fromIntegral b :: Float+ a' = (fromIntegral a :: Float) / 255.0++braillizeGreyScale :: Image Pixel8 -> [T.Text]+braillizeGreyScale =+ map T.pack . getCompose . fmap renderChunk . Compose . chunkifyGreyScale++resizeImageWidth :: Pixel a => Int -> Image a -> Image a+resizeImageWidth width' image+ | width /= width' =+ let ratio :: Float+ ratio = fromIntegral width' / fromIntegral width+ height' = floor (fromIntegral height * ratio)+ y_interval :: Float+ y_interval = fromIntegral height / fromIntegral height'+ x_interval :: Float+ x_interval = fromIntegral width / fromIntegral width'+ resizedData =+ [ imgData V.! idx+ | y <- [0 .. (height' - 1)]+ , x <- [0 .. (width' - 1)]+ , let idx =+ floor (fromIntegral y * y_interval) * width ++ floor (fromIntegral x * x_interval)+ ]+ in Image width' height' $ V.fromList resizedData+ | otherwise = image+ where+ width = imageWidth image+ height = imageHeight image+ imgData = imageData image++braillizeDynamicImage :: DynamicImage -> [T.Text]+braillizeDynamicImage = braillizeGreyScale . resizeImageWidth 60 . greyScaleImage++braillizeByteString :: BS.ByteString -> Either String [T.Text]+braillizeByteString bytes = braillizeDynamicImage <$> decodeImage bytes++braillizeFile :: FilePath -> IO [T.Text]+braillizeFile filePath = do+ bytes <- BS.readFile filePath+ either error return $ braillizeByteString bytes