تحليل علامات 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