Analisando os marcadores JPEG com AttOparsec
-
26-09-2019 - |
Pergunta
Como projeto para promover meu conhecimento e conforto com a Haskell, estou trabalhando para implementar um decodificador JPEG que será útil para o futuro trabalho de visão computacional.
O primeiro passo que escolhi é analisar todos os "marcadores" na imagem. Estes são indicados pelo byte 0XFF, seguidos por um byte não 0. Um byte 0xFF seguido de um byte 0x00 deve ser tratado como dados normais.
O problema que estou concordando é que, ao encontrar uma combinação 0xff 0x00, a análise parece terminar completamente e não são encontrados marcadores mais válidos (se você executar em um JPEG padrão, verá o início do marcador de imagem analisado, mas não o fim do Marcador de imagem como frequentemente 0xff 0x00 ocorre dentro dos próprios dados da imagem).
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
Solução
Desde parseMarker
Considera entrada, mas pode falhar no meio do caminho, você deve poder "retratar" e tentar uma análise diferente quando encontrar um 0xff 0x00.
Não tenho o AtToparsec instalado, mas presumo que seja semelhante ao Parsec, o que não é o retorno por padrão.
parseSection =
skipMany (notWord8 0xFF <|> try (word8 0xFF >> word8 0x0)) >> parseMarker
Outras dicas
O problema é que você não diz para analisar uma sequência de 0xff, 0x00, é tarde, então espero que o próximo respondente coloque palavras (talvez isso ajude você o suficiente), mas aqui está um alternativo parseMarker
e acompanhando 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)
-- ...
Em uma nota lateral, em perguntas, ajuda um pouco a fazer um teste totalmente funcional no código, algo 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