-- Random and Binary IO with IterateeM -- A sample application of the Iteratee-based TIFF library -- The library gives the user the TIFF dictionary, which the user -- can search for specific tags and obtain the values associated with -- the tags, including the pixel matrix. -- -- The overarching theme is incremental processing: initially, -- only the TIFF dictionary is read. The value associated with a tag -- is read only when that tag is looked up (unless the value was short -- and was packed in the TIFF dictionary entry). The pixel matrix -- (let alone the whole TIFF file) is not loaded in memory -- -- the pixel matrix is not even located before it is needed. -- The matrix is processed incrementally, by a user-supplied -- iteratee. -- -- We show a representative application of the library: reading a sample -- TIFF file, printing selected values from the TIFF dictionary, -- verifying the values of selected pixels and computing the histogram -- of pixel values. The pixel verification procedure stops reading the -- pixel matrix as soon as all specified pixel values are verified. -- The histogram accumulation does read the entire matrix, but -- incrementally. Neither pixel matrix processing procedure loads -- the whole matrix in memory. In fact, we never read and retain -- more than the IO-buffer-full of raw data. module TiffTest where import Tiff import IterateeM import RandomIO (EIO, test_driver_random, takeR) import Control.Monad.Trans import Data.Int import Data.Word import Data.Ratio import qualified Data.IntMap as IM -- Our sample code prints interesting information from the TIFF -- dictionary (such as the dimensions, the resolution and the name -- of the image) -- The sample file is a GNU logo (from http://www.gnu.org) -- converted from JPG to TIFF. Copyleft by GNU. sample_tiff_file = "gnu-head-sm.tif" -- The main user function. tiff_reader is the library function, -- which builds the TIFF dictionary. -- process_tiff is the user function, to extract useful data -- from the dictionary test_tiff = test_driver_random (tiff_reader >>= process_tiff) sample_tiff_file -- Sample TIFF processing function process_tiff dict = do note ["dict size: ", show $ IM.size dict] -- Check tag values against the known values for the sample image check_tag TG_IMAGEWIDTH (flip dict_read_int dict) 129 check_tag TG_IMAGELENGTH (flip dict_read_int dict) 122 check_tag TG_BITSPERSAMPLE (flip dict_read_int dict) 8 check_tag TG_IMAGEDESCRIPTION (flip dict_read_string dict) "JPEG:gnu-head-sm.jpg 129x122" check_tag TG_COMPRESSION (flip dict_read_int dict) 1 check_tag TG_SAMPLESPERPIXEL (flip dict_read_int dict) 1 check_tag TG_STRIPBYTECOUNTS (flip dict_read_int dict) 15738 -- nrows*ncols check_tag TG_XRESOLUTION (flip dict_read_rat dict) (72%1) check_tag TG_YRESOLUTION (flip dict_read_rat dict) (72%1) (n,hist) <- compute_hist dict note ["computed histogram over ", show n, " values\n", show hist] note ["Verifying values of sample pixels"] verify_pixel_vals dict [(0,255), (17,248)] where check_tag tag action v = do vc <- action tag case vc of Just v' | v' == v -> note ["Tag ",show tag, " value ", show v] _ -> error $ unwords ["Tag", show tag, "unexpected:", show vc] -- sample processing of the pixel matrix: computing the histogram compute_hist :: TIFFDict -> Iteratee Word8 EIO (Int,IM.IntMap Int) compute_hist dict = runI =<< pixel_matrix_enum dict (compute_hist' 0 IM.empty) where compute_hist' count hist = ie_cont (step count hist) step count hist (Chunk []) = ie_ret $ compute_hist' count hist step count hist (Chunk ch) = ie_ret $ compute_hist' (count + length ch) (foldr accum hist ch) step count hist s = ie_doneM (count,hist) s accum e h = IM.insertWith (+) (fromIntegral e) 1 h -- Another sample processor of the pixel matrix: verifying values of -- some pixels -- This processor does not read the whole matrix; it stops as soon -- as everything is verified or the error is detected verify_pixel_vals dict pixels = runI =<< pixel_matrix_enum dict (verify 0 (IM.fromList pixels)) where verify _ m | IM.null m = return () verify n m = ie_cont (step n m) step n m (Chunk []) = ie_ret $ verify n m step n m (Chunk (h:t)) = case IM.updateLookupWithKey (\k e -> Nothing) n m of (Just v,m) -> if v == h then step (succ n) m (Chunk t) else ie_ret $ throwErrStr $ unwords ["Pixel #",show n, "expected:",show v, "found", show h] (Nothing,m)-> step (succ n) m (Chunk t) step n m s = ie_doneM () s note :: [String] -> Iteratee el EIO () note = liftIO . putStrLn . concat {- -- The end of the expected output -- We observe that verify_pixel_vals has indeed read only 28 bytes -- of the pixel matrix (since it only needed to check 0th and 17th value). computed histogram over 15738 values fromList [(0,174),(1,27),(2,24),(3,16),(4,31),(5,27),(6,17),(7,24),(8,14),(9,23),(10,18),(11,13),(12,14),(13,9),(14,13),(15,14),(16,11),(17,15),(18,14),(19,9),(20,9),(21,14),(22,11),(23,8),(24,8),(25,12),(26,11),(27,7),(28,12),(29,9),(30,12),(31,17),(32,6),(33,12),(34,3),(35,13),(36,5),(37,7),(38,8),(39,10),(40,14),(41,7),(42,5),(43,8),(44,6),(45,13),(46,8),(47,10),(48,10),(49,9),(50,8),(51,6),(52,8),(53,17),(54,13),(55,11),(56,9),(57,13),(58,10),(59,8),(60,17),(61,16),(62,11),(63,12),(64,13),(65,16),(66,15),(67,25),(68,19),(69,17),(70,15),(71,10),(72,12),(73,15),(74,19),(75,16),(76,16),(77,20),(78,12),(79,10),(80,19),(81,17),(82,13),(83,8),(84,16),(85,16),(86,18),(87,10),(88,21),(89,12),(90,19),(91,16),(92,17),(93,16),(94,16),(95,14),(96,14),(97,15),(98,21),(99,16),(100,16),(101,18),(102,15),(103,16),(104,20),(105,17),(106,22),(107,16),(108,20),(109,12),(110,12),(111,12),(112,14),(113,18),(114,16),(115,12),(116,16),(117,16),(118,16),(119,12),(120,11),(121,21),(122,11),(123,19),(124,11),(125,11),(126,6),(127,19),(128,15),(129,15),(130,8),(131,10),(132,11),(133,12),(134,11),(135,22),(136,16),(137,23),(138,11),(139,11),(140,13),(141,15),(142,20),(143,19),(144,22),(145,17),(146,20),(147,13),(148,16),(149,21),(150,18),(151,26),(152,16),(153,15),(154,22),(155,17),(156,17),(157,17),(158,15),(159,19),(160,15),(161,21),(162,19),(163,21),(164,13),(165,18),(166,22),(167,34),(168,21),(169,21),(170,29),(171,18),(172,19),(173,24),(174,23),(175,17),(176,23),(177,26),(178,28),(179,21),(180,19),(181,25),(182,19),(183,28),(184,26),(185,33),(186,28),(187,13),(188,21),(189,22),(190,29),(191,21),(192,15),(193,22),(194,24),(195,24),(196,15),(197,21),(198,11),(199,19),(200,13),(201,14),(202,20),(203,12),(204,18),(205,18),(206,29),(207,17),(208,21),(209,18),(210,15),(211,24),(212,13),(213,25),(214,21),(215,24),(216,20),(217,20),(218,28),(219,19),(220,29),(221,17),(222,24),(223,14),(224,23),(225,17),(226,18),(227,17),(228,23),(229,24),(230,28),(231,33),(232,30),(233,36),(234,36),(235,48),(236,59),(237,70),(238,48),(239,66),(240,84),(241,86),(242,114),(243,131),(244,141),(245,165),(246,176),(247,209),(248,217),(249,259),(250,278),(251,302),(252,330),(253,351),(254,368),(255,8148)] Verifying values of sample pixels Processing the pixel matrix, 15738 bytes Read buffer, offset 8 Read buffer, size 5 Read buffer, size 5 Read buffer, size 5 Read buffer, size 5 Finished reading file -}