Parsing marqueurs JPEG avec attoparsec
-
26-09-2019 - |
Question
En tant que projet pour approfondir mes connaissances et de confort avec Haskell je travaille à la mise en œuvre d'un décodeur JPEG qui sera utile pour les travaux futurs de la vision par ordinateur.
La première étape I ai choisi est d'analyser tous les « marqueurs » dans l'image. Ceux-ci sont indiquées par l'octet 0xFF suivi d'un octet non-0. Un octet 0xFF suivi d'un octet de 0x00 doit être traitée en tant que données normales.
Le problème que je suis en cours d'exécution en est que lors de la rencontre d'une combinaison 0xFF 0x00, l'analyse syntaxique semble finir complètement et ne sont trouvé plusieurs marqueurs valides (si vous exécutez sur un JPEG standard, vous verrez le début de marqueur d'image analysable, mais pas le marqueur de fin de l'image aussi souvent 0xFF 0x00 se produit dans les données d'image lui-même).
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
La solution
Depuis parseMarker
consomme entrée mais peut échouer pendant l'opération, vous devez être en mesure de « revenir en arrière » et essayer de nouveau une analyse syntaxique différente lorsque vous rencontrez un 0xFF 0x00.
Je n'ai pas Attoparsec installé mais je suppose qu'il est similaire à parsec qui ne backtrack pas par défaut.
parseSection =
skipMany (notWord8 0xFF <|> try (word8 0xFF >> word8 0x0)) >> parseMarker
Autres conseils
Le problème est que vous ne dites pas à analyser une séquence de 0xFF, 0x00, il est tard si nous espérons que le prochain answerer va mettre des mots (il peut-être vous aide assez), mais voici un parseMarker
autre et d'accompagnement 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)
-- ...
Sur une note côté, dans les questions qu'il aide un peu à un test entièrement fonctionnel dans le code, quelque chose comme:
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