Analisi di marcatori JPEG con Attoparsec
-
26-09-2019 - |
Domanda
Come progetto per promuovere la mia conoscenza e conforto con Haskell, sto lavorando per implementare un decodificatore JPEG che tornerà utile per il futuro lavoro di visione artificiale.
Il primo passo che ho scelto è quello di analizzare tutti i "marcatori" all'interno dell'immagine. Questi sono indicati dal byte 0xFF seguito da un byte non 0. Un byte 0xFF seguito da un byte 0x00 deve essere trattato come dati normali.
Il problema in cui mi sto imbattendo è che dopo aver incontrato una combinazione 0xFF 0x00, l'analisi sembra finire completamente e non si trovano più marcatori validi (se si esegue su un JPEG standard, vedrai l'inizio del marcatore di immagini analizzato, ma non la fine di Marker di immagine come spesso 0xFF 0x00 si verifica all'interno dei dati dell'immagine stessa).
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
Soluzione
Da parseMarker
consuma input ma potrebbe fallire a metà strada, devi essere in grado di "riavvolgere" e riprovare uno slancio diverso quando si incontra un 0xFF 0x00.
Non ho installato ATTOPASSEC, ma presumo che sia simile a Parsec che non è tornato indietro per impostazione predefinita.
parseSection =
skipMany (notWord8 0xFF <|> try (word8 0xFF >> word8 0x0)) >> parseMarker
Altri suggerimenti
Il problema è che non lo dici di analizzare una sequenza di 0xFF, 0x00, è tardi, quindi spero che il prossimo risposta metta le parole (forse ti aiuta abbastanza), ma ecco un alternativo parseMarker
e accompagnare 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)
-- ...
In una nota a margine, nelle domande aiuta leggermente ad avere un test completamente funzionale nel codice, qualcosa come:
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