packages feed

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 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