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

https://gist.github.com/767488

¿Fue útil?

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top