- Rational Bloom Filter Video Compression เป็นเวิร์กโฟลว์การบีบอัด วิดีโอดิบ โดยผลลัพธ์หลังการกู้คืนจะต้องเหมือนต้นฉบับทุกบิตแบบไม่สูญเสีย
- แกนหลักคือโครงสร้างที่ใช้ จำนวนฟังก์ชันแฮชที่ไม่เป็นจำนวนเต็ม กับ Bloom filter เพื่อมุ่งหวังอัตราการบีบอัดที่ดีกว่าวิธีเดิมในทางทฤษฎี
- รองรับ raw video content เช่น Y4M, YUV, HDR และอธิบายว่าสามารถลดพื้นที่ได้ 40~50% สำหรับวิดีโอทั่วไป
- อิมพลีเมนเทชันทำงานบน Python 3.7+ และต้องใช้ไลบรารีอย่าง
numpy, opencv-python, xxhash, Pillow, scikit-image รวมถึง pyexr สำหรับ HDR
- มีเบนช์มาร์กเปรียบเทียบกับ FFV1, HuffYUV และโหมด lossless ของ H.264 ดังนั้นก่อนนำไปใช้งานจริงควรตรวจสอบผลลัพธ์และขั้นตอนการทำซ้ำใน
results.md
ภาพรวมของ Rational Bloom Filter Video Compression
- โปรเจ็กต์นี้อิมพลีเมนต์วิธีบีบอัดวิดีโอแบบไม่สูญเสียที่อิงกับ rational Bloom filter
- Bloom filter ถูกใช้เป็น โครงสร้างข้อมูลเชิงความน่าจะเป็น สำหรับแทนข้อมูลไบนารีอย่างมีประสิทธิภาพ
- จุดที่แตกต่างคือใช้ rational hash function ที่ไม่ใช่จำนวนเต็มใน Bloom filter
- เป้าหมายคือให้ผลลัพธ์หลังบีบอัดและกู้คืน bit-exact ตรงกับต้นฉบับ
ประเภทที่รองรับและความสามารถในการบีบอัด
- ระบบบีบอัดรองรับ raw video content เช่น Y4M, YUV, HDR
- ความสามารถที่มีให้มีดังนี้
- true lossless compression ที่รับประกันการกู้คืนตรงกันทุกบิต
- ลดพื้นที่ได้ 40~50% สำหรับคอนเทนต์วิดีโอทั่วไป
- รองรับการเข้ารหัสและถอดรหัสแบบมัลติเธรด
- รองรับหลาย color space เช่น RGB, BGR, YUV
- รองรับการประมวลผลคอนเทนต์ HDR
- การรองรับ HDR ยังมีข้อจำกัดว่า “ต้องทำงานเพิ่มอีกเพื่อให้เร็วและพร้อมใช้งาน”
ข้อกำหนดการติดตั้ง
- สภาพแวดล้อมที่ใช้คือ Python 3.7+
- แพ็กเกจที่ต้องใช้มีดังนี้
numpy
opencv-python
matplotlib
pandas
tqdm
requests
xxhash
Pillow
scikit-image
pyexr: สำหรับรองรับ HDR
- ติดตั้ง dependencies ได้ด้วยคำสั่งต่อไปนี้
pip install -r requirements.txt
วิธีใช้งานพื้นฐาน
- ในโค้ด Python ให้นำเข้า
ImprovedVideoCompressor แล้วเริ่มต้นตัวบีบอัด
- การตั้งค่าตัวอย่างประกอบด้วย
noise_tolerance=10.0, keyframe_interval=30, use_direct_yuv=True, verbose=True
compress_video() ใช้บีบอัดวิดีโออินพุตเป็นไฟล์ .bfvc
decompress_video() ใช้กู้คืนไฟล์ .bfvc
- ใช้
verify_lossless() เพื่อตรวจสอบว่าเฟรมต้นฉบับและเฟรมที่กู้คืนแล้ว ไม่สูญเสียข้อมูล หรือไม่
from improved_video_compressor import ImprovedVideoCompressor
compressor = ImprovedVideoCompressor(
noise_tolerance=10.0,
keyframe_interval=30,
use_direct_yuv=True,
verbose=True
)
compressor.compress_video(
input_file="input_video.y4m",
output_file="compressed.bfvc"
)
compressor.decompress_video(
input_file="compressed.bfvc",
output_file="decompressed.mp4"
)
original_frames = compressor.extract_frames_from_video("input_video.y4m")
decompressed_frames = compressor.decompress_video("compressed.bfvc")
verification = compressor.verify_lossless(original_frames, decompressed_frames)
print(f"Lossless: {verification['lossless']}")
การใช้งานผ่านบรรทัดคำสั่ง
- การบีบอัดวิดีโอรันได้ดังนี้
python -m improved_video_compressor compress input_video.y4m output.bfvc --max-frames 30
- ไฟล์ raw YUV ต้องระบุ ความกว้าง ความสูง และฟอร์แมต ร่วมด้วย
python -m improved_video_compressor process-yuv input.yuv output.bfvc --width 1920 --height 1080 --format YUV444
เบนช์มาร์กและตัวเปรียบเทียบ
- ในโปรเจ็กต์มีระบบเบนช์มาร์กสำหรับเปรียบเทียบการบีบอัด Rational Bloom Filter กับ วิธีบีบอัดแบบไม่สูญเสีย อื่น ๆ
- ตัวเปรียบเทียบคือ FFV1, HuffYUV และโหมด lossless ของ H.264
- คำสั่งสำหรับรันเบนช์มาร์กทั้งหมดมีดังนี้
python benchmark_compression.py
- หรือจะระบุเฉพาะชุดข้อมูลและวิธีที่ต้องการก็ได้
python benchmark_compression.py --datasets y4m --methods bloom ffv1 --max-frames 10
- ผลเบนช์มาร์กแบบละเอียดและวิธีทำซ้ำอยู่ใน results.md
ลำดับการทำงานของวิธีบีบอัด
- สคีมการบีบอัดทำงานตามขั้นตอนต่อไปนี้
- Frame Extraction: ดึงเฟรมจากวิดีโออินพุต
- Keyframe Selection: คีย์เฟรมจะถูกเก็บเป็นเฟรมที่บีบอัดด้วย zlib โดยตรง
- Bloom Filter Compression: อินเตอร์เฟรมจะบีบอัดแผนที่ความแตกต่างด้วย rational Bloom filter
- Lossless Verification: ตรวจสอบการกู้คืนแบบ bit-exact ระหว่างการถอดรหัส
- rational Bloom filter ใช้ จำนวนฟังก์ชันแฮชที่ไม่เป็นจำนวนเต็ม
k* เพื่อปรับสมดุลระหว่างพื้นที่และความแม่นยำให้เหมาะสมที่สุด
- อิมพลีเมนเทชันจะใช้ฟังก์ชันแฮช
⌊k*⌋ ตัวแบบกำหนดแน่นอน และใช้ฟังก์ชันแฮชเพิ่มเติมด้วยความน่าจะเป็น k* - ⌊k*⌋
โครงสร้างไฟล์ของโปรเจ็กต์
improved_video_compressor.py: main implementation ของอัลกอริทึมการบีบอัด
verify_true_lossless.py: สคริปต์สำหรับตรวจสอบการกู้คืนแบบไม่สูญเสีย
benchmark_compression.py: ระบบเบนช์มาร์กสำหรับเปรียบเทียบหลายวิธีบีบอัด
download_*.py: สคริปต์ดาวน์โหลดชุดข้อมูลทดสอบ
results.md: ผลเบนช์มาร์กโดยละเอียดและการวิเคราะห์
ใบอนุญาตและการอ้างอิง
- ใบอนุญาตคือ MIT License และสามารถดูรายละเอียดได้ในไฟล์
LICENSE
- หากใช้โค้ดนี้ในงานวิจัย README แนะนำให้ใช้อ้างอิงในรูปแบบ BibTeX ที่รวมไว้
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ดูเหมือนว่าเอกสารจะอธิบายไอเดียที่เรียบง่ายมากได้ไม่ค่อยดีนัก ถ้าผมเข้าใจถูก ก่อนอื่นคือสร้าง บิตแมป ที่มองแต่ละบิตเป็นพิกเซลของภาพ แล้วเมื่อไปจากเฟรม 0 ไปเฟรม 1 พิกเซลที่เปลี่ยนให้เป็น 1 ไม่เช่นนั้นเป็น 0
จากนั้นนำออฟเซ็ตของตำแหน่งที่เป็น 1 ไปแฮชแล้วใส่ลงใน Bloom filter ดังนั้นอินเด็กซ์เหล่านั้นและอินเด็กซ์ผลบวกลวงในสัดส่วนหนึ่งจะถูกมองว่าเป็นบวก
หลังจากนั้นคิวรี Bloom filter เพื่อหาอินเด็กซ์ทั้งหมดที่เป็นบวก แล้วเก็บข้อมูลพิกเซลดิบที่เปลี่ยนสำหรับพิกเซลเหล่านั้น ก็จะสร้างเฟรมถัดไปขึ้นมาใหม่ได้ง่าย
มองได้ว่าเป็นการเก็บเดลตาระหว่างสองเฟรมเป็น x,y,r,g,b ของพิกเซลทั้งหมดที่เปลี่ยน โดยบีบอัดส่วน x,y อย่างมาก และยอมเก็บ r,g,b เพิ่มเกินความจำเป็นเล็กน้อย
ตำแหน่งของพิกเซลที่เปลี่ยนจากเฟรม 0→1 มักจะคล้ายกับตำแหน่งที่จะเปลี่ยนจากเฟรม 1→2 ดังนั้นน่าจะยังมีช่องให้บีบอัดมากขึ้นได้อีก โดยตั้งแฟล็กที่เหมาะสมในเฟรมถัดไป แล้วเก็บเฉพาะออฟเซ็ตที่ต่างไปจากเดิมเพิ่มเติมแบบตรง ๆ
การแปลงกลับเริ่มจากภาพพิกเซลขนาดเล็ก แล้วใช้จำนวนค่าสัมประสิทธิ์เท่ากันเพื่อแปลงเป็นภาพที่มีความกว้างหรือความสูงเป็นสองเท่า และทำซ้ำไปเรื่อย ๆ
ประเด็นสำคัญคือข้อมูลส่วนใหญ่เป็นค่าสัมประสิทธิ์ และส่วนใหญ่ของค่าสัมประสิทธิ์นั้นใกล้ 0 จนสามารถปัดให้เป็น 0 ได้ ทีนี้ปัญหาก็กลายเป็นว่าจะเข้ารหัสตำแหน่งที่ไม่ใช่ 0 อย่างไร ซึ่งจะกลายเป็นโครงสร้างแบบบิตแมปกับอาร์เรย์ของค่าที่ไม่ใช่ 0
อัลกอริทึมสำหรับเข้ารหัสค่าที่ไม่ใช่ 0 มีระดับความอนุรักษ์นิยมต่างกันไป แต่โดยทั่วไปใช้ประโยชน์จากคุณสมบัติที่ว่าค่าเหล่านั้นมักกระจุกตัวกันพอสมควร ซึ่งตรงข้ามกับฟังก์ชันแฮชทั่วไปที่ใช้กับ Bloom filter อย่างสิ้นเชิง
การบีบอัดภาพแบบนี้ช้ามากเพราะ locality แย่มากทั้งในการแปลงเองและการบีบอัดค่าสัมประสิทธิ์ เลยรู้สึกเหมือนเป็นทางตัน
ผมคิดว่า Bloom filter อาจใช้เป็นส่วนหนึ่งของกลยุทธ์บีบอัดแบบไฮบริดที่ซับซ้อนได้ ตัวบีบอัดแบบนั้นยิ่งมีเครื่องมือมากก็ยิ่งดี แต่โดยเฉลี่ยแล้วไม่น่าจะทำให้ดีขึ้นมากนัก
อินพุตวิดีโอดูเหมือนจะทำงานได้ดีขึ้นเพราะเป็น วิดีโอที่ถูกบีบอัดแล้วถอดกลับ จาก YouTube อยู่แล้ว
ถ้าอินพุตเป็นวิดีโอต้นฉบับ สมมติฐานที่ว่า “ระหว่างเฟรมต่อเนื่อง พิกเซลส่วนใหญ่เปลี่ยนเพียงเล็กน้อยหรือไม่เปลี่ยนเลย จึงเกิดเมทริกซ์ผลต่างแบบ sparse” น่าจะใช้ไม่ได้
ถ้าเป็นสัญญาณที่สะอาดมาก เช่น เซนเซอร์สัญญาณรบกวนต่ำกับฉากสว่าง ก็อาจเป็นไปได้ แต่สัญญาณส่วนใหญ่ในโลกจริงมี noise มากกว่า 1 LSB จึงคาดว่าบิตล่าง ๆ อย่างน้อยราวครึ่งหนึ่งจะเปลี่ยน
เมื่อให้วิดีโอผ่านกระบวนการบีบอัดและถอดกลับหนึ่งรอบ noise แบบนั้นมักถูกกำจัดออกไป ทำให้ได้วิดีโอที่นิ่งในเชิงประดิษฐ์ซึ่งทำให้สมมติฐานนี้เป็นจริง
ดูเหมือนว่าพิกเซลที่ค่าเฉลี่ยการเปลี่ยนแปลงของ r,g,b ต่ำกว่า 10 จะไม่ถูกเก็บผลต่างไว้ ถ้าอย่างนั้นแม้พิกเซลหนึ่งจะเปลี่ยนจากสีน้ำเงินล้วน (#00ff00) เป็นสีแดงล้วน (#ff0000) ในเฟรมต่อเนื่องกัน ก็อาจถูกกู้คืนเป็นสีน้ำเงินล้วนทั้งสองเฟรมได้
วิดีโอแบบไม่สูญเสียข้อมูลเหมาะกับคอนเทนต์ดิจิทัลอย่างการอัดหน้าจอมากกว่า สมมติฐานที่ว่าพิกเซลที่เปลี่ยนระหว่างเฟรมต่อเนื่องมีน้อยก็สมเหตุสมผลกว่ากับกรณีนั้น
ถ้าไม่ได้ตั้งใจเปิดใช้เองและยอมรับขนาดไฟล์กับภาระการประมวลผล ก็อาจไม่รู้ด้วยซ้ำว่ายังมีแนวคิดเรื่องข้อมูลต้นฉบับหรือข้อมูลดิบอยู่
ก่อนหน้านี้ไม่เคยคิดแบบนี้มาก่อน
หรือไม่ก็ซื้อกล้องแล้วถ่ายวิดีโอ 8K ต้นฉบับของฉากในชีวิตประจำวันเอง
ถ้าดูจากกราฟ [1] วิธีบีบอัดใหม่นี้ไม่ได้แย่กว่าแค่ใช้ GZIP อย่างเคร่งครัดเสมอไปหรือ?
[1] https://github.com/ross39/new_bloom_filter_repo/blob/main/co...
“ข้อสังเกตสำคัญ: ในสตริงไบนารี หากความหนาแน่นของค่า 1 ต่ำ โดยเฉพาะเมื่อ p* ≈ ต่ำกว่า 0.32453 การเข้ารหัสเฉพาะตำแหน่งของค่า 1 จะมีประสิทธิภาพกว่าการเก็บสตริงดิบ”
สิ่งที่ JPEG/MPEG ทำอยู่ส่วนใหญ่คือการจัดเรียงปัญหาใหม่เพื่อให้สามารถสร้างลำดับ 0 ยาว ๆ ได้ วิธีสแกนบล็อก DCT ให้สอดคล้องกับตำแหน่งขององค์ประกอบ AC/DC อาจเป็นหนึ่งในส่วนที่ล้ำสมัยที่สุดในเทคนิคบีบอัดวิดีโอและภาพหลายแบบ
พูดให้ดีกว่านั้น เทคนิคนี้ไม่มีอะไรที่เฉพาะกับเฟรมวิดีโอเลย ไอเดียเดียวกันนี้สามารถใช้บีบอัดผลต่างระหว่างบิตสตรีมสองชุดที่มีความยาวเท่ากันได้เช่นกัน
ถึงอย่างนั้น ปัญหานี้ก็ไม่น่าจะดีกว่าวิธีบีบอัดที่มีอยู่ เช่น การนำสองบล็อกมาต่อกันแล้ว gzip เพราะเพื่อให้บีบอัดได้ การกระจายของอินพุต—ในที่นี้คือชุดของตำแหน่งบิตที่แตกต่างกัน—ต้องคาดเดาได้มากและไม่เป็นแบบสุ่ม แต่เมื่อส่งข้อมูลผ่านฟังก์ชันแฮช คุณสมบัตินั้นก็จะถูกทำลาย โดยเฉพาะแฮชที่แข็งแรงเชิงเข้ารหัสมีเป้าหมายให้เอาต์พุตแยกไม่ออกจากข้อมูลสุ่ม
สิ่งที่ DCT และการแปลงการแทนค่าสีทำคือเปลี่ยนรายละเอียดปลีกย่อยให้เป็นความถี่สูง และเปลี่ยนรายละเอียดสำคัญให้เป็นความถี่ต่ำ จากนั้นคุณภาพภาพและอัตราการบีบอัดก็กลายเป็นเรื่องง่าย ๆ ว่าจะทิ้งการแทนความถี่สูงไปมากแค่ไหน
นอกจากนี้ JPEG ยังใช้ตาราง Huffman เพื่อลดขนาดภาพลงอีก
เท่าที่รู้ มันไม่ได้ทำอะไรเป็นพิเศษเพื่อสร้างลำดับ 0 ยาว ๆ ดังนั้นการจัดให้ 0 มาเรียงกันจึงไม่ได้ช่วยมากนัก
บรรทัดนี้ทำให้สับสน: https://github.com/ross39/new_bloom_filter_repo/blob/4798d90...
แบบนี้การบีบอัดจะกลายเป็น การบีบอัดแบบสูญเสียข้อมูล และน่าจะทิ้งการเปลี่ยนผ่าน เช่น จาก #ffffff ไปเป็น #fffffa ไป ส่วนบรรทัดก่อนหน้านั้นที่นำข้อมูลพิกเซลมาหาค่าเฉลี่ยก็น่าจะทิ้งการเปลี่ยนผ่านจาก #ff0000 ไปเป็น #00ff00 โดยไม่ขึ้นกับค่า threshold ด้วย
ไม่แน่ใจว่าผมเข้าใจหน้าที่ของบรรทัดโค้ดนั้นผิดหรือเปล่า สิ่งที่กลายเป็น 0 ในมาสก์ผลลัพธ์ดูเหมือนจะไม่ได้ถูกเข้ารหัสลงใน Bloom filter
มีเขียนวิธีคำนวณอัตราการบีบอัดไว้แล้ว แต่สงสัยว่ามีตัวอย่าง อัตราการบีบอัดกรณีแย่สุด/เฉลี่ย/ดีที่สุด ด้วยหรือไม่
แก้ไข: เห็นว่ามีรูปอยู่ใน repository แล้ว ถ้าใส่ไว้ใน README น่าจะช่วยได้
ตั้งใจจะทำการทดสอบให้มากกว่านี้อย่างจริงจัง แล้วทำให้มันเป็นรูปธรรมกว่านี้มาก ตอนนี้ยังเป็นงานที่กำลังทำอยู่และค่อนข้างรกมาก
ผมเป็นผู้เขียนเอง ได้รับฟีดแบ็กดี ๆ มาเยอะ จึงตัดสินใจว่าจะโฟกัสกับการทดสอบที่เข้มงวดขึ้นบน วิดีโอต้นฉบับ และวิดีโอที่มี noise ไปสักระยะหนึ่ง จะอัปเดต repository บ่อย ๆ ต่อไป
ยังอยู่ในช่วงต้นมาก แต่ในการทดสอบวิดีโอต้นฉบับได้ผลค่อนข้างดีพร้อมเบาะแสบางอย่าง: อัตราการบีบอัด 4.8% หรือขนาดลดลง 95.2%, ความเร็วบีบอัด 8.29fps, ความเร็วคลายบีบอัด 9.16fps, ต้องใช้คีย์เฟรมเพียง 4% ของเฟรมทั้งหมด, และเอาต์พุตแบบสูญเสียไม่ได้ในเชิงการรับรู้ (PSNR 31.10dB)
เมื่อเทียบกับโค้ดมาตรฐาน: Rational Bloom Filter 4.8%, JPEG2000 แบบไม่สูญเสียข้อมูล 3.7%, FFV1 แบบไม่สูญเสียข้อมูล 36.5%, H.265/HEVC แบบสูญเสียข้อมูล 9.2%, H.264 แบบสูญเสียข้อมูล 0.3%
ยังมีข้อจำกัดปัจจุบันและงานในอนาคต ผลการบีบอัดดูมีแนวโน้มดี แต่ในการจัดการช่องสี ยังไม่ใช่แบบไม่สูญเสียข้อมูลอย่างแท้จริง การใช้งานปัจจุบันมีปัญหาในกระบวนการแปลงค่าสีจาก YUV เป็น BGR และความแม่นยำของการแปลงค่าสีทำให้เกิดข้อผิดพลาดจากการปัดเศษเล็กน้อย ส่งผลให้ค่าพิกเซลเหลือความแตกต่างเฉลี่ยประมาณ 4.7
นอกจากนี้ การใช้งานปัจจุบันยังจัดการช่องสีในรูปแบบ BGR หลังการแปลง ทำให้เกิดการสูญเสียความแม่นยำเพิ่มเติม
ต่อไปตั้งใจจะจัดการ YUV โดยตรงโดยไม่แปลงเป็น BGR, จัดการข้อมูลสีให้ แม่นตรงระดับบิต, ปรับพารามิเตอร์ของ Bloom filter ให้เข้ากับรูปแบบ chroma subsampling และสร้างระบบเฉพาะสำหรับตรวจสอบแต่ละช่องสีอย่างอิสระ
อยากพิสูจน์ให้ได้ว่าไม่สูญเสียข้อมูลในเชิงคณิตศาสตร์ แต่ยังอีกไกล ตั้งใจจะขุดไอเดียการบีบอัดแบบไม่สูญเสียข้อมูลนี้ต่อไป และยังมีไอเดียบางอย่างสำหรับนำ Rational Bloom Filter ไปใช้ในด้านอื่นด้วย
โค้ดекอย่าง H.264 ก็สามารถรันใน โหมดไม่สูญเสียข้อมูลจริง ๆ ได้เหมือนกัน เพียงแต่แทบไม่มีใครใช้แบบนั้น
เป็นแนวคิดที่น่ารักดี แต่ถ้ามี สตริงไบนารีแบบเบาบาง ก็มีโอกาสสูงที่วิธีดั้งเดิมจะทำได้ดีกว่า
repository ตามอ่านยาก แต่ดูเหมือนอัตราการบีบอัดจะคำนวณจากการดูว่าสามารถทิ้ง ผลต่างของพิกเซล ได้มากแค่ไหน
น่าสนใจอยู่ แต่ตัวเปรียบเทียบที่สำคัญกว่าน่าจะเป็นขนาดไบต์เฉลี่ยของแต่ละเฟรมในวิดีโอ YouTube ที่ถูกบีบอัดแล้ว หากไม่มีการเปรียบเทียบนี้ ก็ยากจะตัดสินว่าเป็นการปรับปรุงจากวิธีปัจจุบันหรือไม่
ถ้าอัลกอริทึมเป็นแบบสูญเสียข้อมูล กล่าวคือกดผลต่างเล็ก ๆ ให้เป็น 0 ก็คงไม่ใช่แบบไม่สูญเสียข้อมูล และน่าจะต้องนำไปเทียบกับอัลกอริทึมแบบสูญเสียข้อมูลอื่น ๆ