Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: CI

on:
push:
branches: [ main, develop, feature/* ]
pull_request:
branches: [ main, develop ]

jobs:
test:
name: Test with Python ${{ matrix.python-version }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential g++

- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install numpy pybind11 pytest pillow

- name: Build C++ extension
run: |
make clean
make all

- name: Create test image
run: |
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)"

- name: Run tests
run: |
python -m pytest tests/ -v --tb=short

- name: Test import
run: |
python -c "import sys; sys.path.insert(0, 'src/python'); import fast_jpeg_decoder as fjd; print('Import successful'); print('Version:', fjd.__version__)"

build-check:
name: Build Check
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install numpy pybind11

- name: Build extension
run: |
make clean
make all

- name: Check build artifacts
run: |
ls -lh src/python/fast_jpeg_decoder/_fast_jpeg_decoder*.so
38 changes: 38 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

# C++ build artifacts
*.o
*.so
*.a
*.dylib

# Python build artifacts
__pycache__/
*.pyc
*.pyo
*.pyd
*.egg-info/
dist/
build/
*.egg

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Test images
*.jpg
*.jpeg
*.png
!tests/test_data/*.jpg
!tests/test_data/*.jpeg
!tests/test_data/*.png

# OS
.DS_Store
Thumbs.db

# Documentation
info.md
82 changes: 82 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Makefile for Fast JPEG Decoder

# Compiler settings
CXX = g++
CXXFLAGS = -std=c++11 -O3 -Wall -fPIC
INCLUDES = -Isrc/cpp

# Python settings
PYTHON = python3
PYTHON_CONFIG = python3-config
PYTHON_INCLUDES = $(shell $(PYTHON_CONFIG) --includes)
PYTHON_LDFLAGS = $(shell $(PYTHON_CONFIG) --ldflags)

# pybind11 settings
PYBIND11_INCLUDES = $(shell $(PYTHON) -m pybind11 --includes)

# Source files
CPP_SOURCES = src/cpp/bitstream.cpp \
src/cpp/idct.cpp \
src/cpp/huffman.cpp \
src/cpp/decoder.cpp

BINDING_SOURCES = src/bindings/bindings.cpp

# Object files
CPP_OBJECTS = $(CPP_SOURCES:.cpp=.o)
BINDING_OBJECTS = $(BINDING_SOURCES:.cpp=.o)

# Output
EXTENSION_SUFFIX = $(shell $(PYTHON_CONFIG) --extension-suffix)
TARGET = src/python/fast_jpeg_decoder/_fast_jpeg_decoder$(EXTENSION_SUFFIX)

# Targets
.PHONY: all clean install test develop

all: $(TARGET)

# Build Python extension
$(TARGET): $(CPP_OBJECTS) $(BINDING_OBJECTS)
$(CXX) -shared -fPIC $(CXXFLAGS) $^ -o $@ $(PYTHON_LDFLAGS)

# Build C++ objects
src/cpp/%.o: src/cpp/%.cpp
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@

# Build binding objects
src/bindings/%.o: src/bindings/%.cpp
$(CXX) $(CXXFLAGS) $(INCLUDES) $(PYTHON_INCLUDES) $(PYBIND11_INCLUDES) -c $< -o $@

# Install package
install: all
$(PYTHON) setup.py install

# Develop mode (editable install)
develop: all
$(PYTHON) -m pip install -e .

# Run tests
test: develop
$(PYTHON) -m pytest tests/ -v

# Clean build artifacts
clean:
rm -f $(CPP_OBJECTS) $(BINDING_OBJECTS)
rm -f $(TARGET)
rm -rf build/ dist/ *.egg-info
rm -rf src/python/fast_jpeg_decoder/*.so
rm -rf src/python/fast_jpeg_decoder/*.pyd
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type f -name "*.pyc" -delete

# Help
help:
@echo "Fast JPEG Decoder - Makefile"
@echo ""
@echo "Targets:"
@echo " all - Build the Python extension (default)"
@echo " install - Install the package"
@echo " develop - Install in development mode (editable)"
@echo " test - Run tests"
@echo " clean - Remove build artifacts"
@echo " help - Show this help message"
111 changes: 110 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,111 @@
# Fast-Jpeg-Decoder
# Fast JPEG Decoder

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

視訊壓縮期末專案

高效能 JPEG 解碼器,核心計算使用 C++ 實現,透過 pybind11 提供 Python API。

## 特點

- **高效能**: 核心解碼邏輯使用 C++ 實現
- **易用性**: 提供簡潔的 Python API
- **正確性**: 完整實現 JPEG Baseline DCT 解碼流程
- **可擴展**: 模組化設計,便於後續優化(OpenMP, SIMD)

## 安裝

### 依賴

- Python 3.8+
- NumPy
- pybind11
- C++ 編譯器(支援 C++11)

### 從原始碼安裝

```bash
# 安裝依賴
pip install numpy pybind11

# 編譯並安裝
make develop
```

## 使用方法

### 基本用法

```python
import fast_jpeg_decoder as fjd

# 從檔案載入 JPEG 圖片
image = fjd.load('photo.jpg')
print(image.shape) # (height, width, 3)

# 從 bytes 載入
with open('photo.jpg', 'rb') as f:
data = f.read()
image = fjd.load_bytes(data)
```

### 使用 Decoder 類別

```python
import fast_jpeg_decoder as fjd

decoder = fjd.JPEGDecoder()
decoder.decode_file('photo.jpg')

print(f"Width: {decoder.width}")
print(f"Height: {decoder.height}")
print(f"Channels: {decoder.channels}")

image = decoder.get_image_data()
```

## 測試

```bash
# 運行測試
make test

# 或直接使用 pytest
pytest tests/ -v
```

## 專案結構

```
Fast-Jpeg-Decoder/
├── src/
│ ├── cpp/ # C++ 核心實現
│ ├── bindings/ # pybind11 綁定
│ └── python/ # Python 包裝
├── tests/ # 單元測試
├── benchmarks/ # 效能測試
├── Makefile # 建構腳本
└── setup.py # Python 安裝腳本
```

## JPEG 解碼流程

1. 解析文件結構(Markers)
2. 霍夫曼解碼(Bitstream)
3. RLE 解碼
4. 反 ZigZag 排序
5. 反量化(Dequantization)
6. 反離散餘弦變換(IDCT)
7. 取樣重建(Upsampling)
8. 色彩空間轉換(YCbCr → RGB)

## 開發計劃

- [x] Phase 1: 基礎實現(Naive 版本)
- [x] Phase 2: CI/CD
- [ ] Phase 3: 效能優化(OpenMP, SIMD)
- [ ] Phase 4: 基準測試與比較

## License

MIT License
69 changes: 69 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""
範例:使用 Fast JPEG Decoder 解碼 JPEG 圖片
"""

import sys
import os
import numpy as np

# Add src/python to path for development
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'python'))

try:
import fast_jpeg_decoder as fjd
except ImportError:
print("Error: fast_jpeg_decoder 模組未安裝")
print("請先執行: make develop")
sys.exit(1)


def main():
"""主函數"""
if len(sys.argv) < 2:
print("用法: python example.py <jpeg_file>")
print("\n範例:")
print(" python example.py photo.jpg")
return

filename = sys.argv[1]

print(f"正在解碼: {filename}")

try:
# 方法 1: 使用簡便函數
image = fjd.load(filename)
print(f"✓ 解碼成功!")
print(f" 圖片尺寸: {image.shape[1]} x {image.shape[0]}")
print(f" 通道數: {image.shape[2]}")
print(f" 資料類型: {image.dtype}")
print(f" 數值範圍: [{image.min()}, {image.max()}]")

# 方法 2: 使用 Decoder 類別
print("\n使用 Decoder 類別:")
decoder = fjd.JPEGDecoder()
success = decoder.decode_file(filename)

if success:
print(f"✓ 解碼成功!")
print(f" 寬度: {decoder.width}")
print(f" 高度: {decoder.height}")
print(f" 通道: {decoder.channels}")

image2 = decoder.get_image_data()

# 驗證兩種方法得到相同結果
if np.array_equal(image, image2):
print("\n✓ 兩種方法結果一致")
else:
print("\n✗ 兩種方法結果不同")

except Exception as e:
print(f"✗ 解碼失敗: {e}")
return 1

return 0


if __name__ == '__main__':
sys.exit(main())
Loading