diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,11 +2,13 @@
 This is a library to generate `.png` files from QR codes.
 
 ## Dependencies
-The library depends on the C library [https://github.com/fukuchi/libqrencode](libqrencode) which you will need to install separately, as well as the C library `Zbar` from [here](https://github.com/ZBar/ZBar). You should also be able to get them from your distro. 
+The library depends on the C library [libqrencode](https://github.com/fukuchi/libqrencode) which you will need to install separately, as well as the C library `Zbar` from [here](https://github.com/ZBar/ZBar). You should also be able to get them from your distro. 
 
-## Usage
-The library exports three main functions - `createQRCode`, `readQRString`, and `byteStringToQR` - and their secured/signed versions. The first takes any object that is an instance of `ToJSON` and writes an image to file, while the second takes filepath pointing to an image and returns the text in the QR code. The third takes a (strict) bytestring and writes it to file.
+## Library
+The library can be used via the exported functions `createQRCode`, `byteStringToQR` and `readQRString`, plus their signed counterparts. The first two export to ".png" while the third can be used on any image format `Zbar` supports.
 
+The functions `bsToImg` and `objToImg` output JuicyPixels images for your further manipulation.
+
 ## Executable
 
 ### Installation
@@ -32,5 +34,3 @@
 qrpipe read "nametag.png"
 ```
 
-## Library
-The library can be used via the exported functions `createQRCode`, `byteStringToQR` and `readQRString`, plus their signed counterparts. 
diff --git a/qr-imager.cabal b/qr-imager.cabal
--- a/qr-imager.cabal
+++ b/qr-imager.cabal
@@ -1,5 +1,5 @@
 name:                qr-imager
-version:             0.2.0.0
+version:             0.2.1.0
 synopsis:            Library to generate QR codes from bytestrings and objects
 description:         Please see README.md
 homepage:            https://github.com/vmchale/QRImager#readme
@@ -18,8 +18,8 @@
   hs-source-dirs:      src
   exposed-modules:     Data.QRCodes
                      , Data.QRCodes.Exe
-  other-modules:       Data.QRCodes.Image
-                     , Data.QRCodes.Signature
+                     , Data.QRCodes.Image
+  other-modules:       Data.QRCodes.Signature
                      , Data.QRCodes.Utils
   build-depends:       base >= 4.7 && < 5
                      , aeson
@@ -39,7 +39,7 @@
 executable qrpipe
   hs-source-dirs:      app
   main-is:             Main.hs
-  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -O2
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
   build-depends:       base
                      , qr-imager
   default-language:    Haskell2010
diff --git a/src/Data/QRCodes.hs b/src/Data/QRCodes.hs
--- a/src/Data/QRCodes.hs
+++ b/src/Data/QRCodes.hs
@@ -3,18 +3,20 @@
 
 -- | Module providing several functions for creating QR codes and their signed counterparts
 module Data.QRCodes (-- * Functions on objects
-                      createSecureQRCode
-                    , createQRCode
+                      createQRCode
+                    , createSecureQRCode
+                    , createSecureQRCode'
                     -- * Functions for `ByteStrings`
                     , byteStringToQR
                     , byteStringToQRSec
+                    , byteStringToQRSec'
                     -- * functions to read QR codes
                     , readQRString
                     , readQRStrSec
+                    , readQRStrSec'
                     ) where
 
 import Data.Aeson
-import Data.QRCode
 import Codec.Picture.Png (writePng)
 import Data.ByteString.Lazy (toStrict)
 import qualified Data.ByteString.Char8 as BS
@@ -26,18 +28,32 @@
 import Data.QRCodes.Utils
 import Data.QRCodes.Signature
 import Data.QRCodes.Image
+import Data.Word (Word8)
+import Crypto.PubKey.RSA
 
--- | Creates a signed QR code from a strict bytestring and path to keyfile/path where the keyfile should be generated.
+-- | Creates a signed QR code from a strict bytestring and path to keyfile/path.
+-- If the keyfile does not already exist it will be generated, otherwise it will be read.
+--
 -- Note that QR codes may only contain a small number of characters, so encrypting can sometimes make an object too big to encode.
 --
 -- > byteStringToQRSec (BS.pack "hello") ".key.hk" "qrcode.png"
 byteStringToQRSec :: BS.ByteString -> FilePath -> FilePath -> IO ()
-byteStringToQRSec string keyfile filepath = (flip byteStringToQR filepath) =<< (((fmap preserveUpper) . (flip mkSig keyfile)) string)
+byteStringToQRSec string keyfile filepath = (flip byteStringToQR filepath) =<< (((fmap preserveUpper) . (flip mkSigFile keyfile)) string)
 
+-- | Create a signed QR code from a strict `ByteString` and a key
+--
+-- > byteStringToQRSec' (BS.pack "Vanessa") (generate 256 0x10001)
+byteStringToQRSec' :: BS.ByteString -> (PublicKey, PrivateKey) -> FilePath -> IO ()
+byteStringToQRSec' string key filepath = (flip byteStringToQR filepath) =<< (((fmap preserveUpper) . (flip mkSig key)) string)
+
 -- | Creates a signed QR code from an object that is part of the ToJSON class
 createSecureQRCode :: (ToJSON a) => a -> FilePath -> FilePath -> IO ()
 createSecureQRCode object = byteStringToQRSec (toStrict $ encode object)
 
+-- | Creates a signed QR code from an object that is part of the ToJSON class
+createSecureQRCode' :: (ToJSON a) => a -> (PublicKey, PrivateKey) -> FilePath -> IO ()
+createSecureQRCode' object = byteStringToQRSec' (toStrict $ encode object)
+
 -- | Creates a QR code from an object that is part of the ToJSON class
 --
 -- > createQRCode userRecord "user-231.png"
@@ -46,10 +62,7 @@
 
 -- | Creates a QR code from a strict bytestring
 byteStringToQR :: BS.ByteString -> FilePath -> IO ()
-byteStringToQR input filepath = do
-    smallMatrix <- toMatrix <$> encodeByteString input Nothing QR_ECLEVEL_H QR_MODE_EIGHT False
-    let qrMatrix = fattenList 8 $ map (fattenList 8) smallMatrix
-    writePng filepath (encodePng qrMatrix)
+byteStringToQR input filepath = (bsToImg input) >>= writePng filepath
 
 -- | given a filepath, read the QR code as a string in all lowercase
 --
@@ -63,4 +76,10 @@
 readQRStrSec :: FilePath -> FilePath -> IO String
 readQRStrSec filepath keyfile = do
     enc <- (map toLower) . init . (drop 8) . (view _2) <$> readCreateProcessWithExitCode (shell $ "zbarimg " ++ filepath) ""
-    (fmap $ liftEither show) . (flip checkSig keyfile) . resolveUpper $ (BS.pack) enc
+    (fmap $ liftEither show) . (flip checkSigFile keyfile) . resolveUpper $ (BS.pack) enc
+
+-- | Read an image containing a QR code, decode and verify the signature using the given key.
+readQRStrSec' :: FilePath -> (PublicKey, PrivateKey) -> IO String
+readQRStrSec' filepath key = do
+    enc <- (map toLower) . init . (drop 8) . (view _2) <$> readCreateProcessWithExitCode (shell $ "zbarimg " ++ filepath) ""
+    (fmap $ liftEither show) . (flip checkSig key) . resolveUpper $ (BS.pack) enc
diff --git a/src/Data/QRCodes/Image.hs b/src/Data/QRCodes/Image.hs
--- a/src/Data/QRCodes/Image.hs
+++ b/src/Data/QRCodes/Image.hs
@@ -1,21 +1,69 @@
 -- | A few functions to deal with the image itself
-module Data.QRCodes.Image where
+module Data.QRCodes.Image (-- * Functions to convert to JuicyPixels `Image`
+                          bsToImg
+                          , objToImg
+                          -- * Functions to sign and convert to `Image`
+                          , bsToImgSec
+                          , objToImgSec
+                          -- * Functions to sign with user-supplied key and yield an `Image`
+                          , bsToImgSec'
+                          , objToImgSec'
+                          ) where
 
 import Data.Word (Word8)
 import Codec.Picture.Types as T
 import Prelude as P
 import qualified Data.Vector.Storable as V
+import qualified Data.ByteString as BS
+import Data.ByteString.Lazy (toStrict)
+import Data.QRCode
+import Data.Aeson
+import Data.QRCodes.Utils
+import Data.QRCodes.Signature
+import Crypto.PubKey.RSA
 
--- | Encode a png, given a matrix
+-- | Creates a signed QR code from a strict bytestring and path to keyfile/path where the keyfile should be generated, yielding a JuicyPixels `Image`.
+-- Note that QR codes may only contain a small number of characters, so encrypting can sometimes make an object too big to encode.
+--
+-- > bsToImgSec (BS.pack "hello") ".key.hk"
+bsToImgSec :: BS.ByteString -> FilePath -> IO (T.Image Word8)
+bsToImgSec string keyfile = bsToImg =<< (((fmap preserveUpper) . (flip mkSigFile keyfile)) string)
+
+-- | Sign a byteString with a given key
+--
+-- > bsToImgSec' (BS.pack "str") (generate 256 0x10001)
+bsToImgSec' :: BS.ByteString -> (PublicKey, PrivateKey) -> IO (T.Image Word8)
+bsToImgSec' string key = bsToImg =<< (((fmap preserveUpper) . (flip mkSig key)) string)
+
+-- | Encode an object as a JuicyPixels `Image` with a key in a given file.
+objToImgSec :: (ToJSON a) => a -> FilePath -> IO (T.Image Word8)
+objToImgSec obj = bsToImgSec (toStrict $ encode obj)
+
+-- | Encode an object as a JuicyPixels `Image` with a key.
+objToImgSec' :: (ToJSON a) => a -> (PublicKey, PrivateKey) -> IO (T.Image Word8)
+objToImgSec' obj = bsToImgSec' (toStrict $ encode obj)
+
+-- | Create a JuicyPixels `Image` from a `ByteString`
+bsToImg :: BS.ByteString -> IO (T.Image Word8)
+bsToImg input = do
+    smallMatrix <- toMatrix <$> encodeByteString input Nothing QR_ECLEVEL_H QR_MODE_EIGHT False
+    let qrMatrix = fattenList 8 $ map (fattenList 8) smallMatrix
+    pure $ encodePng qrMatrix
+
+-- | Encode an object as a JuicyPixels `Image`
+objToImg :: (ToJSON a) => a -> IO (T.Image Word8)
+objToImg obj = let input = toStrict $ encode obj in bsToImg input
+
+-- | Encode a JuicyPixels `Image` given a matrix
 encodePng :: [[Word8]] -> T.Image Word8
 encodePng matrix = Image dim dim vector
     where dim    = P.length matrix
           vector = V.map ((*255) . swapWord) $ V.fromList $ P.concat matrix
 
--- | To help scale the image up
+-- | To help scale the image up, e.g.
+--
+-- > fattenList 8 $ (map fattenList 8) smallMatrix
+--
+-- to scale @smallMatrix :: [[Word8]]@ by a factor of 8
 fattenList :: Int -> [a] -> [a]
 fattenList i l = P.concat $ P.foldr ((:) . (P.replicate i)) [] l
-
-swapWord :: Word8 -> Word8
-swapWord 1 = 0
-swapWord 0 = 1
diff --git a/src/Data/QRCodes/Signature.hs b/src/Data/QRCodes/Signature.hs
--- a/src/Data/QRCodes/Signature.hs
+++ b/src/Data/QRCodes/Signature.hs
@@ -4,30 +4,45 @@
 import Jose.Jws
 import qualified Data.ByteString.Char8 as BS
 import Jose.Jwt (unJwt, JwtError)
-import Crypto.PubKey.RSA as Cr
+import Crypto.PubKey.RSA
 import Control.Lens
 import System.Directory
 import Jose.Jwa (JwsAlg (RS256))
 import Data.QRCodes.Utils
 
+-- | Verify a signed token with a key
+checkSigFile :: BS.ByteString -> FilePath -> IO (Either JwtError BS.ByteString)
+checkSigFile tok filepath = do
+    key <- read <$> readFile filepath :: IO (PublicKey, PrivateKey)
+    checkSig tok key
+
 -- | Verify a signed token with a key from a given filepath
-checkSig :: BS.ByteString -> FilePath -> IO (Either JwtError BS.ByteString)
-checkSig tok filepath = do
-    key <- read <$> readFile filepath :: IO (Cr.PublicKey, Cr.PrivateKey)
+checkSig :: BS.ByteString -> (PublicKey, PrivateKey) -> IO (Either JwtError BS.ByteString)
+checkSig tok key = do
     let jws = rsaDecode (view _1 key) tok
     return $ fmap (view _2) jws
 
--- | Sign a token (note that we must pass in a processed token since there is no uppercase/lowercase here) with a key from a given filepath
-mkSig :: BS.ByteString -> FilePath -> IO BS.ByteString
-mkSig string filepath = do
+-- | Sign a token. 
+-- If the key file does not exist, a new key will be generated.
+mkSigFile :: BS.ByteString -> FilePath -> IO BS.ByteString
+mkSigFile string filepath = do
     switch <- doesFileExist filepath
     if not switch then do
         putStrLn "generating key..."
-        key <- Cr.generate 256 0x10001
+        key <- generate 256 0x10001
         writeFile filepath (show key)
     else
         return ()
-    key' <- read <$> readFile filepath :: IO (Cr.PublicKey, Cr.PrivateKey)
-    signedToken <- rsaEncode RS256 (view _2 key') string
+    key' <- read <$> readFile filepath :: IO (PublicKey, PrivateKey)
+    mkSig string key'
+
+-- | Sign a token with a key.
+-- (note that we must pass in a processed token since there is no uppercase/lowercase for QR codes)
+--
+-- > mkSig (BS.pack "hello") (generate 256 0x10001)
+mkSig :: BS.ByteString -> (PublicKey, PrivateKey) -> IO BS.ByteString
+mkSig string key = do
+    signedToken <- rsaEncode RS256 (view _2 key) string
     let signed = fmap unJwt signedToken
     liftEither id (return <$> signed)
+  
diff --git a/src/Data/QRCodes/Utils.hs b/src/Data/QRCodes/Utils.hs
--- a/src/Data/QRCodes/Utils.hs
+++ b/src/Data/QRCodes/Utils.hs
@@ -4,6 +4,7 @@
 import qualified Data.ByteString.Char8 as BS
 import Data.Char (toLower, toUpper)
 import Data.List.Utils (replace)
+import Data.Word (Word8)
 
 -- | function applied to byteStrings before saving to QR code so that uppercase/lowercase signatures can be preserverd
 preserveUpper :: BS.ByteString -> BS.ByteString
@@ -23,3 +24,6 @@
 liftEither :: (Show b, Monad m) => (t -> m a) -> Either b t -> m a
 liftEither = either (error . show)
 
+swapWord :: Word8 -> Word8
+swapWord 1 = 0
+swapWord 0 = 1
