Analizar marcadores JPEG con AttoParsec
-
26-09-2019 - |
Pregunta
Como proyecto para promover mi conocimiento y consuelo con Haskell, estoy trabajando para implementar un decodificador JPEG que será útil para el futuro trabajo de visión por computadora.
El primer paso que he elegido es analizar todos los "marcadores" dentro de la imagen. Estos se indican por el byte 0xff seguido de un byte no 0. Un byte 0xff seguido de un byte 0x00 se tratará como datos normales.
El problema con el que me encuentro es que al encontrar una combinación 0xff 0x00, el análisis parece terminar por completo y no se encuentran más marcadores válidos (si se ejecuta en un JPEG estándar, verá el inicio del marcador de imagen analizado, pero no el final de Marcador de imagen con tanta frecuencia 0xff 0x00 ocurre dentro de los datos de la imagen en sí).
import System.Environment
import System.IO
import Control.Applicative hiding (many)
import Data.Attoparsec as A
import qualified Data.ByteString as BS
parseMarker = do
part1 <- word8 0xFF
part2 <- notWord8 0x0
return (part1, part2)
parseSection = do
A.skipWhile (\x -> x /= 0xFF) *> parseMarker
parseBody = do
many parseSection
parseJPEG jpeg = do
handleParseResult $ feed (parse parseBody jpeg) BS.empty
handleParseResult result = do
case result of
Fail _ _ msg -> msg
Done _ r -> show r
_ -> ""
main = do
(filename : _ ) <- getArgs
handle <- openFile filename ReadMode
contents <- BS.hGetContents handle
putStrLn $ parseJPEG contents
hClose handle
Solución
Ya que parseMarker
consume aportes pero puede fallar a la parte superior, debe poder "rebobinar" y volver a intentar un análisis diferente cuando encuentre un 0xff 0x00.
No tengo instalado AtTopARSEC, pero supongo que es similar a PARSEC que no retrocede de forma predeterminada.
parseSection =
skipMany (notWord8 0xFF <|> try (word8 0xFF >> word8 0x0)) >> parseMarker
Otros consejos
El problema es que no le dice que analice una secuencia de 0xff, 0x00, es tarde, así que espero que el próximo respondedor le ponga palabras (tal vez le ayude lo suficiente), pero aquí hay una alternativa alternativa. parseMarker
y acompañante handleParseResult
:
parseMarker = do -- or call it "parsePotentialMarker"
part1 <- word8 0xFF
part2 <- anyWord8
return $
if (part2 /= 0)
then [(part1, part2)]
else []
-- ... skipping other functions...
handleParseResult result = do
case result of
Fail _ _ msg -> msg
Done _ r -> show (concat r)
-- ...
En una nota al margen, en preguntas, ayuda ligeramente tener una prueba completamente funcional en el código, algo así como:
main =
let contents = BS.pack [1,2,3,4,0xFF,1 {- marker 1 -},0xFF,0x00,0xFF,2 {- marker 2 -},31,13,0xFF,0x00]
in putStrLn $ parseJPEG contents