Разборки маркеров JPEG с ATTOPARSEC
-
26-09-2019 - |
Вопрос
В качестве проекта к дальнейшему мнению знаний и комфорта с 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
Решение
С parseMarker
Потребляет вход, но может потерпеть неудачу, вы должны иметь возможность «перемотать» и повторить другой разбор, когда вы столкнулись с 0xFF 0x00.
У меня нет устанавливаемых ATTOPARSEC, но я предполагаю, что он похож на Parsec, который не возвращает по умолчанию.
parseSection =
skipMany (notWord8 0xFF <|> try (word8 0xFF >> word8 0x0)) >> parseMarker
Другие советы
Проблема в том, что вы не говорите об этом разбирать последовательность 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