🇻🇳 Building an AI-Powered Vietnamese Handwriting Assessment Platform: A Complete Guide – PART 4-5
🔧 Part 4: Model Integration & Scoring System Deep Dive
Understanding the HandwritingScorer Architecture
Before implementing the frontend, let’s examine how our Vietnamese handwriting model actually works in production. The HandwritingScorer class serves as the core engine for analysis:
# Core model architecture (from handwriting_analysis.py)
class HandwritingCNN(nn.Module):
"""CNN model for handwriting quality assessment - Updated for Vietnamese model"""
def __init__(self, num_features=7, pretrained=True, dropout_rate=0.3):
super(HandwritingCNN, self).__init__()
# Use EfficientNet-B0 as backbone with pretrained ImageNet weights
self.backbone = models.efficientnet_b0(weights=EfficientNet_B0_Weights.DEFAULT)
# Vietnamese-optimized classifier architecture
num_ftrs = self.backbone.classifier[1].in_features # 1280 features
self.backbone.classifier = nn.Sequential(
nn.Dropout(dropout_rate),
nn.Linear(num_ftrs, 512), # First dense layer
nn.ReLU(),
nn.BatchNorm1d(512),
nn.Dropout(dropout_rate),
nn.Linear(512, 256), # Second dense layer
nn.ReLU(),
nn.BatchNorm1d(256),
nn.Dropout(dropout_rate),
nn.Linear(256, 7), # 7 Vietnamese metrics output
nn.Sigmoid() # Output between 0-1
)
Model Initialization and Loading Process
class HandwritingScorer:
def __init__(self, model_path: str = None, device: str = "cpu"):
"""Initialize scorer with Vietnamese model configuration"""
self.device = torch.device(device)
self.model = HandwritingCNN(num_outputs=7) # 7-metric Vietnamese model
# Load calibration settings for Vietnamese scoring
self._load_calibration_config()
# Load trained weights or use random initialization
if model_path and os.path.exists(model_path):
checkpoint = torch.load(model_path, map_location=self.device)
if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
self.model.load_state_dict(checkpoint['model_state_dict'])
else:
self.model.load_state_dict(checkpoint)
logger.info(f"✅ Loaded trained Vietnamese model: {model_path}")
self.model.to(self.device)
self.model.eval()
Image Processing and Analysis Pipeline
The analysis process follows these critical steps:
Step 1: Image Preprocessing and Tensor Conversion
def analyze_handwriting(self, image_array: np.ndarray) -> Dict[str, float]:
"""Main analysis pipeline for Vietnamese handwriting"""
# Critical: Convert image format for PyTorch
if len(image_array.shape) == 3: # (H, W, C) -> (C, H, W)
tensor = torch.from_numpy(image_array).permute(2, 0, 1).unsqueeze(0)
elif len(image_array.shape) == 2: # (H, W) grayscale -> (1, 1, H, W)
tensor = torch.from_numpy(image_array).unsqueeze(0).unsqueeze(0)
# Normalize to [0, 1] range expected by model
tensor = tensor.to(self.device).float() / 255.0
logger.info(f"🔍 Processing tensor shape: {tensor.shape}")
Step 2: Model Inference and Raw Scoring
# Run Vietnamese handwriting analysis
with torch.no_grad():
raw_scores = self.model(tensor) # Shape: [1, 7]
raw_scores = raw_scores.squeeze().cpu().numpy() # Shape: [7]
logger.info(f"🔍 Raw model outputs (0-1): {raw_scores}")
# Map to Vietnamese handwriting metrics
vietnamese_metrics = {
'legibility': float(raw_scores[0]), # Diacritic clarity
'consistency': float(raw_scores[1]), # Character uniformity
'alignment': float(raw_scores[2]), # Text organization
'spacing': float(raw_scores[3]), # Vietnamese syllable spacing
'size_uniformity': float(raw_scores[4]), # Character size consistency
'slant_consistency': float(raw_scores[5]), # Writing angle uniformity
'pressure_consistency': float(raw_scores[6]) # Stroke weight consistency
}
Step 3: Vietnamese Education Calibration
def _calibrate_score(self, raw_score: float) -> float:
"""Convert raw model output to Vietnamese educational scale"""
config = self.calibration
if raw_score >= config["excellent_threshold"]:
# Excellent Vietnamese handwriting (75-100 points)
excellent_min, excellent_max = config["excellent_range"]
threshold_range = 1.0 - config["excellent_threshold"]
score_range = excellent_max - excellent_min
base_score = excellent_min + (raw_score - config["excellent_threshold"]) * (score_range / threshold_range)
return base_score * config["boost_factor"]
elif raw_score >= config["good_threshold"]:
# Good Vietnamese handwriting (55-75 points)
good_min, good_max = config["good_range"]
# ... similar calculation for good range
# ... other ranges for average and poor handwriting
Step 4: Vietnamese-Weighted Overall Score Calculation
# Apply Vietnamese education-specific weights
vietnamese_weights = {
'legibility': 0.2, # 20% - Most important for reading
'consistency': 0.15, # 15% - Character formation uniformity
'alignment': 0.15, # 15% - Text organization
'spacing': 0.15, # 15% - Vietnamese syllable spacing
'size_uniformity': 0.1, # 10% - Character size consistency
'slant_consistency': 0.15, # 15% - Writing angle uniformity
'pressure_consistency': 0.1 # 10% - Stroke consistency
}
# Calculate weighted average for overall Vietnamese score
overall_score = sum(calibrated_scores[key] * vietnamese_weights[key]
for key in vietnamese_weights.keys())
# Round to whole numbers for educational clarity
final_scores = {key: round(score) for key, score in calibrated_scores.items()}
final_scores['overall'] = round(overall_score)
return final_scores
Production API Integration Example
Here’s how the complete analysis flows in your FastAPI endpoint:
@router.post("/analyze", response_model=AnalysisResult)
async def analyze_handwriting(file: UploadFile, user_id: str, db: Session):
"""Complete Vietnamese handwriting analysis pipeline"""
# 1. Save and validate uploaded image
temp_filepath = save_uploaded_file(file)
# 2. Preprocess image for Vietnamese model
processed_image = image_processor.preprocess_image(temp_filepath)
# 3. Run Vietnamese handwriting analysis
scores = handwriting_scorer.analyze_handwriting(processed_image)
# 4. Generate Vietnamese feedback
feedback, suggestions = handwriting_scorer.generate_feedback(scores)
# 5. Return comprehensive results
return AnalysisResult(
overall_score=scores['overall'],
score_breakdown={
"legibility": scores['legibility'],
"consistency": scores['consistency'],
"alignment": scores['alignment'],
"spacing": scores['spacing'],
"size_uniformity": scores['size_uniformity'],
"slant_consistency": scores['slant_consistency'],
"pressure_consistency": scores['pressure_consistency']
},
feedback=feedback,
suggestions=suggestions,
processing_time=processing_time
)
This robust scoring system ensures reliable Vietnamese handwriting assessment while providing detailed insights for educational improvement. The calibration system adapts to different educational contexts, making it suitable for various Vietnamese school environments.
💻 Part 5: Frontend UI Implementation
Modern Vietnamese Educational Interface
Our Next.js frontend provides an intuitive experience for Vietnamese users:
Key Features:
- Complete Vietnamese localization – All text in Vietnamese
- Mobile-first design – Optimized for tablets and phones commonly used in Vietnamese schools
- Real-time analysis – Immediate feedback for students
- Progress tracking – Long-term improvement monitoring
- Teacher dashboard – Class-wide performance analytics
Results Display with 7-Factor Breakdown
Dashboard for Progress Tracking

// Results Component showing Vietnamese analysis
function AnalysisResults({ analysis }: { analysis: AnalysisResult }) {
const metrics = [
{
key: "legibility",
label: "Độ rõ ràng",
description: "Chữ viết có rõ ràng, dễ đọc không",
},
{
key: "consistency",
label: "Tính nhất quán",
description: "Kích thước và hình dáng chữ có đồng đều không",
},
{
key: "alignment",
label: "Căn chỉnh",
description: "Chữ viết có thẳng hàng không",
},
{
key: "spacing",
label: "Khoảng cách",
description: "Khoảng cách giữa các chữ có phù hợp không",
},
{
key: "size_uniformity",
label: "Đồng đều kích thước",
description: "Chiều cao các chữ có giống nhau không",
},
{
key: "slant_consistency",
label: "Độ nghiêng nhất quán",
description: "Góc nghiêng chữ có đều nhau không",
},
{
key: "pressure_consistency",
label: "Lực bút đều",
description: "Lực ấn bút có đều nhau không",
},
];


Dashboard for Progress Tracking

🚀 Next Steps & Advanced Features
Contributing to Vietnamese Education Technology
This project demonstrates how AI can be localized and optimized for specific educational contexts. The combination of:
- Cultural Understanding (Vietnamese writing characteristics)
- Technical Excellence (state-of-the-art CNN architecture)
- Educational Impact (measurable learning improvements)
- Continuous Learning (model improves with every user)
Creates a sustainable platform that grows smarter over time while serving the unique needs of Vietnamese education.
📚 Conclusion
Building an AI-powered Vietnamese handwriting assessment platform requires more than just technical skills—it demands deep understanding of educational needs, cultural context, and sustainable improvement mechanisms.
This guide has walked you through:
✅ Custom Model Training – From data collection to production-ready CNN
✅ 7-Factor Analysis – Comprehensive handwriting evaluation system
✅ Vietnamese Optimization – Language-specific adaptations and improvements
✅ Continuous Learning – Self-improving system through user data
✅ Production UI – Complete web application with mobile optimization
✅ Real-world Impact – Measurable improvements in Vietnamese classrooms
The result is not just a technical achievement, but an educational tool that empowers Vietnamese students, teachers, and parents with AI-driven insights into handwriting development.
Start building your Vietnamese handwriting assessment platform today, and contribute to the future of AI-powered education in Vietnam! 🇻🇳
For the complete source code and detailed implementation guides, visit: AI Handwriting Assessment Platform
Demo site: AI Handwriting Assessment Platform
📝 Note
Some code examples and technical content in this blog post were generated with the assistance of AI to provide comprehensive implementation details and best practices for Vietnamese handwriting assessment systems. The actual project implementation and educational insights are based on real-world development and testing.