Skip to content

Commit 0d9a93b

Browse files
authored
Merge pull request #2 from 5000user5000/dev/naive-decoder
naive decoder 實現
2 parents 208cbad + ce1b0d6 commit 0d9a93b

22 files changed

Lines changed: 2111 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main, develop, feature/* ]
6+
pull_request:
7+
branches: [ main, develop ]
8+
9+
jobs:
10+
test:
11+
name: Test with Python ${{ matrix.python-version }}
12+
runs-on: ubuntu-latest
13+
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
18+
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@v4
22+
23+
- name: Set up Python ${{ matrix.python-version }}
24+
uses: actions/setup-python@v5
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
28+
- name: Install system dependencies
29+
run: |
30+
sudo apt-get update
31+
sudo apt-get install -y build-essential g++
32+
33+
- name: Install Python dependencies
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install numpy pybind11 pytest pillow
37+
38+
- name: Build C++ extension
39+
run: |
40+
make clean
41+
make all
42+
43+
- name: Create test image
44+
run: |
45+
python -c "from PIL import Image; import numpy as np; img = np.random.randint(0, 256, (64, 64, 3), dtype=np.uint8); Image.fromarray(img).save('tests/test_data/sample.jpg', 'JPEG', quality=90)"
46+
47+
- name: Run tests
48+
run: |
49+
python -m pytest tests/ -v --tb=short
50+
51+
- name: Test import
52+
run: |
53+
python -c "import sys; sys.path.insert(0, 'src/python'); import fast_jpeg_decoder as fjd; print('Import successful'); print('Version:', fjd.__version__)"
54+
55+
build-check:
56+
name: Build Check
57+
runs-on: ubuntu-latest
58+
59+
steps:
60+
- name: Checkout code
61+
uses: actions/checkout@v4
62+
63+
- name: Set up Python
64+
uses: actions/setup-python@v5
65+
with:
66+
python-version: '3.11'
67+
68+
- name: Install dependencies
69+
run: |
70+
python -m pip install --upgrade pip
71+
pip install numpy pybind11
72+
73+
- name: Build extension
74+
run: |
75+
make clean
76+
make all
77+
78+
- name: Check build artifacts
79+
run: |
80+
ls -lh src/python/fast_jpeg_decoder/_fast_jpeg_decoder*.so

.gitignore

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
# C++ build artifacts
3+
*.o
4+
*.so
5+
*.a
6+
*.dylib
7+
8+
# Python build artifacts
9+
__pycache__/
10+
*.pyc
11+
*.pyo
12+
*.pyd
13+
*.egg-info/
14+
dist/
15+
build/
16+
*.egg
17+
18+
# IDE
19+
.vscode/
20+
.idea/
21+
*.swp
22+
*.swo
23+
*~
24+
25+
# Test images
26+
*.jpg
27+
*.jpeg
28+
*.png
29+
!tests/test_data/*.jpg
30+
!tests/test_data/*.jpeg
31+
!tests/test_data/*.png
32+
33+
# OS
34+
.DS_Store
35+
Thumbs.db
36+
37+
# Documentation
38+
info.md

Makefile

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Makefile for Fast JPEG Decoder
2+
3+
# Compiler settings
4+
CXX = g++
5+
CXXFLAGS = -std=c++11 -O3 -Wall -fPIC
6+
INCLUDES = -Isrc/cpp
7+
8+
# Python settings
9+
PYTHON = python3
10+
PYTHON_CONFIG = python3-config
11+
PYTHON_INCLUDES = $(shell $(PYTHON_CONFIG) --includes)
12+
PYTHON_LDFLAGS = $(shell $(PYTHON_CONFIG) --ldflags)
13+
14+
# pybind11 settings
15+
PYBIND11_INCLUDES = $(shell $(PYTHON) -m pybind11 --includes)
16+
17+
# Source files
18+
CPP_SOURCES = src/cpp/bitstream.cpp \
19+
src/cpp/idct.cpp \
20+
src/cpp/huffman.cpp \
21+
src/cpp/decoder.cpp
22+
23+
BINDING_SOURCES = src/bindings/bindings.cpp
24+
25+
# Object files
26+
CPP_OBJECTS = $(CPP_SOURCES:.cpp=.o)
27+
BINDING_OBJECTS = $(BINDING_SOURCES:.cpp=.o)
28+
29+
# Output
30+
EXTENSION_SUFFIX = $(shell $(PYTHON_CONFIG) --extension-suffix)
31+
TARGET = src/python/fast_jpeg_decoder/_fast_jpeg_decoder$(EXTENSION_SUFFIX)
32+
33+
# Targets
34+
.PHONY: all clean install test develop
35+
36+
all: $(TARGET)
37+
38+
# Build Python extension
39+
$(TARGET): $(CPP_OBJECTS) $(BINDING_OBJECTS)
40+
$(CXX) -shared -fPIC $(CXXFLAGS) $^ -o $@ $(PYTHON_LDFLAGS)
41+
42+
# Build C++ objects
43+
src/cpp/%.o: src/cpp/%.cpp
44+
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
45+
46+
# Build binding objects
47+
src/bindings/%.o: src/bindings/%.cpp
48+
$(CXX) $(CXXFLAGS) $(INCLUDES) $(PYTHON_INCLUDES) $(PYBIND11_INCLUDES) -c $< -o $@
49+
50+
# Install package
51+
install: all
52+
$(PYTHON) setup.py install
53+
54+
# Develop mode (editable install)
55+
develop: all
56+
$(PYTHON) -m pip install -e .
57+
58+
# Run tests
59+
test: develop
60+
$(PYTHON) -m pytest tests/ -v
61+
62+
# Clean build artifacts
63+
clean:
64+
rm -f $(CPP_OBJECTS) $(BINDING_OBJECTS)
65+
rm -f $(TARGET)
66+
rm -rf build/ dist/ *.egg-info
67+
rm -rf src/python/fast_jpeg_decoder/*.so
68+
rm -rf src/python/fast_jpeg_decoder/*.pyd
69+
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
70+
find . -type f -name "*.pyc" -delete
71+
72+
# Help
73+
help:
74+
@echo "Fast JPEG Decoder - Makefile"
75+
@echo ""
76+
@echo "Targets:"
77+
@echo " all - Build the Python extension (default)"
78+
@echo " install - Install the package"
79+
@echo " develop - Install in development mode (editable)"
80+
@echo " test - Run tests"
81+
@echo " clean - Remove build artifacts"
82+
@echo " help - Show this help message"

README.md

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,111 @@
1-
# Fast-Jpeg-Decoder
1+
# Fast JPEG Decoder
2+
3+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4+
25
視訊壓縮期末專案
6+
7+
高效能 JPEG 解碼器,核心計算使用 C++ 實現,透過 pybind11 提供 Python API。
8+
9+
## 特點
10+
11+
- **高效能**: 核心解碼邏輯使用 C++ 實現
12+
- **易用性**: 提供簡潔的 Python API
13+
- **正確性**: 完整實現 JPEG Baseline DCT 解碼流程
14+
- **可擴展**: 模組化設計,便於後續優化(OpenMP, SIMD)
15+
16+
## 安裝
17+
18+
### 依賴
19+
20+
- Python 3.8+
21+
- NumPy
22+
- pybind11
23+
- C++ 編譯器(支援 C++11)
24+
25+
### 從原始碼安裝
26+
27+
```bash
28+
# 安裝依賴
29+
pip install numpy pybind11
30+
31+
# 編譯並安裝
32+
make develop
33+
```
34+
35+
## 使用方法
36+
37+
### 基本用法
38+
39+
```python
40+
import fast_jpeg_decoder as fjd
41+
42+
# 從檔案載入 JPEG 圖片
43+
image = fjd.load('photo.jpg')
44+
print(image.shape) # (height, width, 3)
45+
46+
# 從 bytes 載入
47+
with open('photo.jpg', 'rb') as f:
48+
data = f.read()
49+
image = fjd.load_bytes(data)
50+
```
51+
52+
### 使用 Decoder 類別
53+
54+
```python
55+
import fast_jpeg_decoder as fjd
56+
57+
decoder = fjd.JPEGDecoder()
58+
decoder.decode_file('photo.jpg')
59+
60+
print(f"Width: {decoder.width}")
61+
print(f"Height: {decoder.height}")
62+
print(f"Channels: {decoder.channels}")
63+
64+
image = decoder.get_image_data()
65+
```
66+
67+
## 測試
68+
69+
```bash
70+
# 運行測試
71+
make test
72+
73+
# 或直接使用 pytest
74+
pytest tests/ -v
75+
```
76+
77+
## 專案結構
78+
79+
```
80+
Fast-Jpeg-Decoder/
81+
├── src/
82+
│ ├── cpp/ # C++ 核心實現
83+
│ ├── bindings/ # pybind11 綁定
84+
│ └── python/ # Python 包裝
85+
├── tests/ # 單元測試
86+
├── benchmarks/ # 效能測試
87+
├── Makefile # 建構腳本
88+
└── setup.py # Python 安裝腳本
89+
```
90+
91+
## JPEG 解碼流程
92+
93+
1. 解析文件結構(Markers)
94+
2. 霍夫曼解碼(Bitstream)
95+
3. RLE 解碼
96+
4. 反 ZigZag 排序
97+
5. 反量化(Dequantization)
98+
6. 反離散餘弦變換(IDCT)
99+
7. 取樣重建(Upsampling)
100+
8. 色彩空間轉換(YCbCr → RGB)
101+
102+
## 開發計劃
103+
104+
- [x] Phase 1: 基礎實現(Naive 版本)
105+
- [x] Phase 2: CI/CD
106+
- [ ] Phase 3: 效能優化(OpenMP, SIMD)
107+
- [ ] Phase 4: 基準測試與比較
108+
109+
## License
110+
111+
MIT License

example.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env python3
2+
"""
3+
範例:使用 Fast JPEG Decoder 解碼 JPEG 圖片
4+
"""
5+
6+
import sys
7+
import os
8+
import numpy as np
9+
10+
# Add src/python to path for development
11+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'python'))
12+
13+
try:
14+
import fast_jpeg_decoder as fjd
15+
except ImportError:
16+
print("Error: fast_jpeg_decoder 模組未安裝")
17+
print("請先執行: make develop")
18+
sys.exit(1)
19+
20+
21+
def main():
22+
"""主函數"""
23+
if len(sys.argv) < 2:
24+
print("用法: python example.py <jpeg_file>")
25+
print("\n範例:")
26+
print(" python example.py photo.jpg")
27+
return
28+
29+
filename = sys.argv[1]
30+
31+
print(f"正在解碼: {filename}")
32+
33+
try:
34+
# 方法 1: 使用簡便函數
35+
image = fjd.load(filename)
36+
print(f"✓ 解碼成功!")
37+
print(f" 圖片尺寸: {image.shape[1]} x {image.shape[0]}")
38+
print(f" 通道數: {image.shape[2]}")
39+
print(f" 資料類型: {image.dtype}")
40+
print(f" 數值範圍: [{image.min()}, {image.max()}]")
41+
42+
# 方法 2: 使用 Decoder 類別
43+
print("\n使用 Decoder 類別:")
44+
decoder = fjd.JPEGDecoder()
45+
success = decoder.decode_file(filename)
46+
47+
if success:
48+
print(f"✓ 解碼成功!")
49+
print(f" 寬度: {decoder.width}")
50+
print(f" 高度: {decoder.height}")
51+
print(f" 通道: {decoder.channels}")
52+
53+
image2 = decoder.get_image_data()
54+
55+
# 驗證兩種方法得到相同結果
56+
if np.array_equal(image, image2):
57+
print("\n✓ 兩種方法結果一致")
58+
else:
59+
print("\n✗ 兩種方法結果不同")
60+
61+
except Exception as e:
62+
print(f"✗ 解碼失敗: {e}")
63+
return 1
64+
65+
return 0
66+
67+
68+
if __name__ == '__main__':
69+
sys.exit(main())

0 commit comments

Comments
 (0)