2

I have an image in the type Image VS Y Double (after using function readImageY from HIP library) and I want to convert it to Image VS Y Word8. How should I go about doing this? I need to have it in this precision for the next function I am applying this to.

Here's a snippet of the relevant code:

import Codec.Picture         
import Codec.Picture.Types
import Control.Arrow
import Data.Ratio 
import Data.Monoid
import Graphics.Image.Processing
import qualified Graphics.Image as I
import Graphics.Image.IO
import Graphics.Image.IO.Formats
import Graphics.Image.Interface.Vector
import qualified Graphics.Image.Interface as Interface
import Data.Word (Word8)
import qualified Data.Matrix as M
import System.FilePath.Posix (splitExtension)

to2DMatrix :: FilePath -> FilePath -> Border(Interface.Pixel I.Y Word8) -> (Int, Int) -> IO ()
to2DMatrix fp fpout bor (dim1, dim2)= do
    eimg <- I.readImageY VS fp
    let new_res :: Interface.Image VS I.Y Word8
        new_res = I.resize Bilinear bor (dim1, dim2) eimg 
    let rle = twoDToMatrix $ pixelToInt $ toJPImageY8 new_res
    let (name, _) = splitExtension fp
    writeFile (name ++ ".txt") (show rle)
    writeImage fpout rle

This is the error:

Couldn't match type ‘Double’ with ‘Word8’
      Expected type: Interface.Image VS I.Y Word8
        Actual type: Interface.Image VS I.Y Double
    • In the fourth argument of ‘resize’, namely ‘eimg’
      In the expression: resize Bilinear bor (dim1, dim2) eimg
      In an equation for ‘new_res’:
          new_res = resize Bilinear bor (dim1, dim2) eimg
   |
29 |         new_res = I.resize Bilinear bor (dim1, dim2) eimg
   |                                                      ^^^^

EDIT: The doubles are the pixel values in grayscale stored as a VS vector type in the Image type. The problem I am having is getting access to the doubles to be able to convert them. Trying to interpret/find a way the HIP library here but I am new to Haskell and can't figure it out.

FifthCode
  • 65
  • 6
  • Are you certain you want to convert `Double` to `Word8`? Because AFAIK `Word8` represents `8-bit unsigned integer` whereas you need 64bits to represent a `Double`. So for that you can use `doubleToWord :: Double -> Word64` from [Data.Serialize.IEEE754](http://hackage.haskell.org/package/cereal-ieee754-0.1/docs/Data-Serialize-IEEE754.html) – atis Jun 11 '20 at 19:23
  • Well, the next function I want to apply, `toJPImage` requires the argument to be of type `Image VS Y Word8`. This function converts the image to type `Image Pixel8` type. – FifthCode Jun 11 '20 at 19:28
  • 1
    You can use function like `round` or `floor` or `ceiling` to convert `Double` to num of class `Integral` (There will be loss of precision) and then use `fromIntegral` to convert that to `Word8` something like this `doubleToWord8 = fromIntegral . round` – atis Jun 11 '20 at 19:35
  • 1
    What are these doubles? Probably a black/white in the range of zero to one? If so consider converting to 0-255 with `conv d = floor (d * 255)` – Thomas M. DuBuisson Jun 11 '20 at 20:46
  • The doubles are the pixel values in grayscale stored as a VS vector type in the Image type. The problem I am having is getting access to the doubles to be able to convert them. Trying to interpret/find a way the HIP library [here](http://hackage.haskell.org/package/hip-1.5.4.0/docs/Graphics-Image-Interface.html#t:Manifest) but I am new to Haskell and can't figure it out. – FifthCode Jun 11 '20 at 21:04
  • 1
    That operation is a `map`. I'm not sure about your imports without seeing your code, but try `Interface.map conv eimg`. – Thomas M. DuBuisson Jun 11 '20 at 21:27
  • I get the follow error: `Couldn't match expected type ‘Interface.Pixel I.Y Word8’ with actual type ‘a0 -> Integer’`. I added `conv :: Interface.Pixel I.Y Double -> Interface.Pixel I.Y Word8 \\\ conv d = fromIntegral . floor (d * 255)` as a function. – FifthCode Jun 11 '20 at 22:53
  • 1
    Give `conv` a type and you'll see why that's wrong. Beginners often confuse function composition with function application. In short, `fromIntegral (floor (d*255))` – Thomas M. DuBuisson Jun 12 '20 at 00:34
  • Thanks! I didn't know the difference between `$` and `.` before vs paranthesis before. I have another error now: `No instance for (RealFrac (Interface.Pixel Y Double)) arising from a use of ‘round’`. Seems like the Pixel type does not have an instance `RealFrac`. – FifthCode Jun 12 '20 at 03:14
  • Thanks! Figured it out. See answer below. – FifthCode Jun 12 '20 at 05:25

1 Answers1

1

There is a class Elevator that is available in Graphics.Image.Interface in the Haskell Image Processing (HIP) library, that allows you to change the pixel precision from/to several precision types.

Here's the snippet of the code that changed:

to2DMatrix :: FilePath  -> (Int, Int) -> IO (Maybe (M.Matrix Int)) 
to2DMatrix fp (dim1, dim2)= do
    eimg <- I.readImageY VS fp 
    let new_res :: Interface.Image VS I.Y Word8
        new_res = I.resize Bilinear Edge  (dim1, dim2) $ Interface.map conv eimg
    let rle = twoDToMatrix $ pixelToInt $ toJPImageY8 new_res
    return $ Just (rle)

conv :: Pixel Y Double -> Pixel Y Word8 
conv d = toWord8 <$> d

The <$> is an infix operator version of fmap. So this is the same as:

conv :: Pixel Y Double -> Pixel Y Word8 
conv d = fmap toWord8 d

Extra details to convert input image to matrix:

pixelToInt changes from type Pixel8 to [[Int]]:

pixelToInt :: Image Pixel8 -> [[Int]]
pixelToInt =
  map reverse . reverse .  snd . pixelFold 
    (\(lastY, ps:pss) x y p ->           
      if y == lastY                        
        then (y, (fromIntegral p:ps):pss)
        else (y, [fromIntegral p]:ps:pss))
    (0,[[]])

Then, you can use Data.Matrix to change from [[Int]] to Data.Matrix Matrix type as needed.

FifthCode
  • 65
  • 6