Build a Fast Multilingual OCR Service with Go, OpenCV, Tesseract, Vue3 & Docker

This step‑by‑step guide shows how to create a high‑performance OCR service that recognizes Chinese and English, using a Go Gin backend with OpenCV preprocessing and Tesseract, a Vue3 frontend, Docker multi‑stage builds, and Swagger UI for API testing.

Code Wrench
Code Wrench
Code Wrench
Build a Fast Multilingual OCR Service with Go, OpenCV, Tesseract, Vue3 & Docker

Project Highlights

Backend : Go + Gin + GoCV + Tesseract – concurrent OCR, image preprocessing, JSON API.

Frontend : Vue3 + Tailwind + Axios – image upload, one‑click recognition, result display.

Build : Docker multi‑stage – single image containing both Go and Node builds.

Documentation : Swagger (OpenAPI 3) + Swagger UI – built‑in API docs and interactive testing.

Deployment : Docker Compose – one‑command start for the OCR service and Swagger UI.

Project Structure

go-ocr-vue/
├── backend/            # Go backend (Gin + Tesseract + OpenCV)
│   ├── main.go
│   ├── swagger.yaml
│   └── go.mod
├── frontend/           # Vue3 + Tailwind frontend
│   ├── index.html
│   ├── package.json
│   ├── vite.config.js
│   ├── tailwind.config.js
│   └── src/
│       ├── App.vue
│       └── main.js
├── Dockerfile          # Multi‑stage build
└── docker-compose.yml  # One‑click service start

Backend: Go OCR Core Logic

The OCR pipeline consists of three stages:

Image preprocessing – grayscale conversion, Gaussian blur, adaptive threshold.

Tesseract recognition – wrapped by the gosseract client.

Worker pool – Goroutine pool to handle concurrent requests and improve throughput.

Key preprocessing code:

gray := gocv.NewMat()
gocv.CvtColor(img, &gray, gocv.ColorBGRToGray)
blur := gocv.NewMat()
gocv.GaussianBlur(gray, &blur, image.Pt(3, 3), 0, 0, gocv.BorderDefault)
bin := gocv.NewMat()
gocv.AdaptiveThreshold(blur, &bin, 255, gocv.AdaptiveThresholdMean, gocv.ThresholdBinary, 15, 10)

Tesseract invocation:

client := gosseract.NewClient()
defer client.Close()
client.SetLanguage("chi_sim", "eng")
client.SetPageSegMode(gosseract.PSM_AUTO)
client.SetImageFromBytes(imgBytes)
text, _ := client.Text()

Gin API endpoint:

r.POST("/ocr", func(c *gin.Context) {
    file, _ := c.FormFile("image")
    f, _ := file.Open()
    imgBytes, _ := io.ReadAll(f)
    text := doOCR(imgBytes)
    c.JSON(200, gin.H{"text": text})
})

Frontend: Vue3 + Tailwind UI

Minimal interface for uploading an image, triggering OCR, and displaying the result.

<template>
  <div class="min-h-screen flex flex-col items-center justify-center bg-gray-100">
    <div class="bg-white p-6 rounded-2xl shadow-lg w-full max-w-md">
      <h1 class="text-2xl font-bold text-center mb-4">Go OCR Service</h1>
      <form @submit.prevent="handleUpload" class="flex flex-col gap-4">
        <input type="file" @change="onFileChange" accept="image/*" />
        <button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white py-2 rounded" :disabled="loading">
          {{ loading ? "识别中..." : "上传并识别" }}
        </button>
      </form>
      <div v-if="result" class="mt-4 bg-gray-50 border p-3 rounded">
        <strong>识别结果:</strong>
        <p>{{ result }}</p>
      </div>
    </div>
  </div>
</template>

Upload logic:

import axios from "axios";
const formData = new FormData();
formData.append("image", file.value);
const res = await axios.post("/ocr", formData);
result.value = res.data.text;

Docker: Multi‑stage Build

Dockerfile builds the frontend with Node, the backend with Go, and packages everything into a single lightweight image.

FROM node:20 AS frontend-builder
WORKDIR /frontend
COPY frontend/ .
RUN yarn install && yarn build

FROM golang:1.22-bullseye AS backend-builder
RUN apt-get update && apt-get install -y libtesseract-dev libleptonica-dev tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng
WORKDIR /app
COPY backend/ .
COPY --from=frontend-builder /frontend/dist ./frontend-dist
RUN go build -o server main.go

FROM debian:bullseye-slim
WORKDIR /app
COPY --from=backend-builder /app/server .
COPY --from=backend-builder /app/frontend-dist ./frontend-dist
EXPOSE 8080
CMD ["./server"]

For environments with slow Debian mirrors, replace the source list:

RUN sed -i 's|http://deb.debian.org/debian|https://mirrors.aliyun.com/debian|g' /etc/apt/sources.list

Swagger Documentation & Online Debugging

The OpenAPI specification is stored in backend/swagger.yaml. Example excerpt:

openapi: 3.0.3
info:
  title: Go OCR Service
  version: "1.0.0"
paths:
  /ocr:
    post:
      summary: Upload image and return OCR text
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              properties:
                image:
                  type: string
                  format: binary
      responses:
        "200":
          description: Success

Docker Compose: One‑Click Startup

version: "3.8"
services:
  app:
    build: .
    ports: ["8080:8080"]

  swagger-ui:
    image: swaggerapi/swagger-ui
    ports: ["8081:8080"]
    environment:
      - SWAGGER_JSON_URL=http://app:8080/swagger.yaml
    depends_on:
      - app

Start the stack:

docker compose up --build

Access points:

Frontend UI – http://localhost:8080 Swagger UI –

http://localhost:8081

Quick Start Commands

# Clone the repository
git clone https://github.com/louis-xie-programmer/go-ocr-vue.git
cd go-ocr-vue

# Build and start the service with Swagger UI
docker compose up --build

Technical Tips & Performance Optimizations

Recognition speed : Use a Goroutine pool to reuse the Tesseract client and avoid repeated initialization.

Image preprocessing : For low‑quality images, apply gocv.EqualizeHist to enhance contrast.

Language packs : Chinese and English are bundled; add tesseract-ocr-chi-tra for Traditional Chinese support.

Deployment optimization : Build a custom base image that already contains OpenCV and Tesseract to speed up Docker builds.

Caching : Store results of identical images (e.g., using an MD5 hash) in Redis to avoid redundant OCR work.

Source Code

GitHub repository: https://github.com/louis-xie-programmer/go-ocr-vue Gitee mirror (China):

https://gitee.com/louis_xie/go-ocr-vue
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerGoOCROpenCVVue3Swaggertesseract
Code Wrench
Written by

Code Wrench

Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.