3

作为一个提高我对 Haskell 知识和舒适度的项目,我正在努力实现一个 JPEG 解码器,它将在未来的计算机视觉工作中派上用场。

我选择的第一步是解析图像中的所有“标记”。这些由字节 0xFF 后跟非 0 字节指示。0xFF 字节后跟 0x00 字节应被视为正常数据。

我遇到的问题是,在遇到 0xFF 0x00 组合时,解析似乎完全完成并且没有找到更多有效标记(如果您在标准 JPEG 上运行,您将看到解析图像标记的开头,但不是结尾图像标记通常 0xFF 0x00 出现在图像数据本身内)。

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

4

2 回答 2

2

由于parseMarker消耗输入但可能中途失败,因此您必须能够“倒带”并在遇到 0xFF 0x00 时重试不同的解析。

我没有安装 Attoparsec,但我认为它类似于 Parsec,默认情况下不会回溯。

parseSection =
    skipMany (notWord8 0xFF <|> try (word8 0xFF >> word8 0x0)) >> parseMarker
于 2011-01-06T05:00:00.783 回答
1

问题是你没有告诉它解析一个 0xFF,0x00 的序列,它已经很晚了,所以希望下一个回答者会给它写词(它可能对你有足够的帮助),但这里有一个替代的parseMarker和伴随的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)
-- ...

附带说明一下,在问题中,在代码中进行完整的功能测试会有所帮助,例如:

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
于 2011-01-06T05:08:31.293 回答