Golang图像处理示例

环境准备
go mod init image-processing
go get golang.org/x/image/...
图像读取与基本操作
package main

import (
    "image"
    "image/jpeg"
    "os"
    "golang.org/x/image/draw"
)

func loadImage(filename string) (image.Image, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    img, _, err := image.Decode(file)
    return img, err
}
几何变换示例
func resizeImage(img image.Image, newWidth, newHeight int) image.Image {
    bounds := img.Bounds()
    dst := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
    
    // 使用双线性插值进行缩放
    draw.CatmullRom.Scale(dst, dst.Bounds(), img, bounds, draw.Over, nil)
    
    return dst
}
滤波算法示例
func convolve(img image.Image, kernel [][]float64) image.Image {
    bounds := img.Bounds()
    width, height := bounds.Dx(), bounds.Dy()

    // 创建输出图像
    dst := image.NewRGBA(bounds)

    // 计算核的中心位置
    kernelSize := len(kernel)
    kernelCenter := kernelSize / 2

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            var rSum, gSum, bSum float64

            for ky := 0; ky < kernelSize; ky++ {
                for kx := 0; kx < kernelSize; kx++ {
                    // 计算图像中的对应位置
                    imgX := x + kx - kernelCenter
                    imgY := y + ky - kernelCenter

                    // 边界处理:使用镜像填充
                    if imgX < bounds.Min.X {
                        imgX = bounds.Min.X + (bounds.Min.X - imgX)
                    } else if imgX >= bounds.Max.X {
                        imgX = bounds.Max.X - 1 - (imgX - bounds.Max.X)
                    }

                    if imgY < bounds.Min.Y {
                        imgY = bounds.Min.Y + (bounds.Min.Y - imgY)
                    } else if imgY >= bounds.Max.Y {
                        imgY = bounds.Max.Y - 1 - (imgY - bounds.Max.Y)
                    }

                    // 获取像素值
                    r, g, b, _ := img.At(imgX, imgY).RGBA()

                    // 应用核权重
                    weight := kernel[ky][kx]
                    rSum += float64(r>>8) * weight
                    gSum += float64(g>>8) * weight
                    bSum += float64(b>>8) * weight
                }
            }

            // 限制值范围并设置像素
            r := uint8(clamp(rSum, 0, 255))
            g := uint8(clamp(gSum, 0, 255))
            b := uint8(clamp(bSum, 0, 255))

            dst.Set(x, y, color.RGBA{r, g, b, 255})
        }
    }

    return dst
}

// 预定义的滤波核
var (
    // 均值滤波核
    meanKernel = [][]float64{
        {1.0 / 9, 1.0 / 9, 1.0 / 9},
        {1.0 / 9, 1.0 / 9, 1.0 / 9},
        {1.0 / 9, 1.0 / 9, 1.0 / 9},
    }

    // 高斯滤波核
    gaussianKernel = [][]float64{
        {1.0 / 16, 2.0 / 16, 1.0 / 16},
        {2.0 / 16, 4.0 / 16, 2.0 / 16},
        {1.0 / 16, 2.0 / 16, 1.0 / 16},
    }

    // 锐化滤波核
    sharpenKernel = [][]float64{
        {-1, -1, -1},
        {-1, 9, -1},
        {-1, -1, -1},
    }

    // 边缘检测滤波核
    edgeKernel = [][]float64{
        {-1, -1, -1},
        {-1, 8, -1},
        {-1, -1, -1},
    }

    // Sobel X 方向
    sobelXKernel = [][]float64{
        {-1, 0, 1},
        {-2, 0, 2},
        {-1, 0, 1},
    }

    // Sobel Y 方向
    sobelYKernel = [][]float64{
        {-1, -2, -1},
        {0, 0, 0},
        {1, 2, 1},
    }
)
边缘检测示例
func edgeDetection(img image.Image) map[string]image.Image {
    bounds := img.Bounds()
    width, height := bounds.Dx(), bounds.Dy()

    // 转换为灰度图
    gray := rgbToGrayscale(img)

    // Sobel 边缘检测
    sobelX := convolve(gray, sobelXKernel)
    sobelY := convolve(gray, sobelYKernel)

    // 计算梯度幅值
    edgeResult := image.NewRGBA(bounds)
    for y := 0; y < height; y++ {
        for x := 0; x < width; x++ {
            rx, gx, bx, _ := sobelX.At(x, y).RGBA()
            ry, gy, by, _ := sobelY.At(x, y).RGBA()

            // 计算梯度幅值
            gradX := 0.299*float64(rx>>8) + 0.587*float64(gx>>8) + 0.114*float64(bx>>8)
            gradY := 0.299*float64(ry>>8) + 0.587*float64(gy>>8) + 0.114*float64(by>>8)
            magnitude := math.Sqrt(gradX*gradX + gradY*gradY)

            magnitude = clamp(magnitude, 0, 255)
            edgeResult.Set(x, y, color.Gray{uint8(magnitude)})
        }
    }

    // Laplacian 边缘检测
    laplacianKernel := [][]float64{
        {0, -1, 0},
        {-1, 4, -1},
        {0, -1, 0},
    }
    laplacian := convolve(gray, laplacianKernel)

    return map[string]image.Image{
        "sobel":     edgeResult,
        "laplacian": laplacian,
    }
}
形态学操作示例
// 腐蚀操作
func erode(img image.Image, kernelSize int) image.Image {
    bounds := img.Bounds()
    dst := image.NewGray(bounds)
    kernelRadius := kernelSize / 2

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            minVal := uint8(255)

            for ky := -kernelRadius; ky <= kernelRadius; ky++ {
                for kx := -kernelRadius; kx <= kernelRadius; kx++ {
                    imgX := x + kx
                    imgY := y + ky

                    if imgX >= bounds.Min.X && imgX < bounds.Max.X &&
                        imgY >= bounds.Min.Y && imgY < bounds.Max.Y {
                        gray := color.GrayModel.Convert(img.At(imgX, imgY)).(color.Gray)
                        if gray.Y < minVal {
                            minVal = gray.Y
                        }
                    }
                }
            }
            dst.SetGray(x, y, color.Gray{minVal})
        }
    }
    return dst
}

// 膨胀操作
func dilate(img image.Image, kernelSize int) image.Image {
    bounds := img.Bounds()
    dst := image.NewGray(bounds)
    kernelRadius := kernelSize / 2

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            maxVal := uint8(0)

            for ky := -kernelRadius; ky <= kernelRadius; ky++ {
                for kx := -kernelRadius; kx <= kernelRadius; kx++ {
                    imgX := x + kx
                    imgY := y + ky

                    if imgX >= bounds.Min.X && imgX < bounds.Max.X &&
                        imgY >= bounds.Min.Y && imgY < bounds.Max.Y {
                        gray := color.GrayModel.Convert(img.At(imgX, imgY)).(color.Gray)
                        if gray.Y > maxVal {
                            maxVal = gray.Y
                        }
                    }
                }
            }
            dst.SetGray(x, y, color.Gray{maxVal})
        }
    }
    return dst
}

// 开运算(先腐蚀后膨胀)
func opening(img image.Image, kernelSize int) image.Image {
    eroded := erode(img, kernelSize)
    return dilate(eroded, kernelSize)
}

// 闭运算(先膨胀后腐蚀)
func closing(img image.Image, kernelSize int) image.Image {
    dilated := dilate(img, kernelSize)
    return erode(dilated, kernelSize)
}
色彩空间转换示例
// RGB 转灰度(加权平均法)
func rgbToGrayscale(img image.Image) image.Image {
    bounds := img.Bounds()
    grayImg := image.NewGray(bounds)

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, _ := img.At(x, y).RGBA()
            grayValue := 0.299*float64(r>>8) + 0.587*float64(g>>8) + 0.114*float64(b>>8)
            grayImg.SetGray(x, y, color.Gray{uint8(grayValue)})
        }
    }
    return grayImg
}

// RGB 转 HSV
func rgbToHSV(r, g, b uint8) (float64, float64, float64) {
    rf := float64(r) / 255.0
    gf := float64(g) / 255.0
    bf := float64(b) / 255.0

    maxVal := math.Max(math.Max(rf, gf), bf)
    minVal := math.Min(math.Min(rf, gf), bf)
    delta := maxVal - minVal

    // 计算明度 V
    v := maxVal

    // 计算饱和度 S
    var s float64
    if maxVal == 0 {
        s = 0
    } else {
        s = delta / maxVal
    }

    // 计算色调 H
    var h float64
    if delta == 0 {
        h = 0
    } else if maxVal == rf {
        h = 60 * math.Mod((gf-bf)/delta, 6)
    } else if maxVal == gf {
        h = 60 * ((bf-rf)/delta + 2)
    } else {
        h = 60 * ((rf-gf)/delta + 4)
    }

    if h < 0 {
        h += 360
    }

    return h, s, v
}

// HSV 转 RGB
func hsvToRGB(h, s, v float64) (uint8, uint8, uint8) {
    c := v * s
    x := c * (1 - math.Abs(math.Mod(h/60, 2)-1))
    m := v - c

    var r, g, b float64
    if h < 60 {
        r, g, b = c, x, 0
    } else if h < 120 {
        r, g, b = x, c, 0
    } else if h < 180 {
        r, g, b = 0, c, x
    } else if h < 240 {
        r, g, b = 0, x, c
    } else if h < 300 {
        r, g, b = x, 0, c
    } else {
        r, g, b = c, 0, x
    }

    return uint8((r + m) * 255), uint8((g + m) * 255), uint8((b + m) * 255)
}
直方图处理示例
// 计算直方图
func computeHistogram(img image.Image) []int {
    histogram := make([]int, 256)
    bounds := img.Bounds()

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, _ := img.At(x, y).RGBA()
            gray := 0.299*float64(r>>8) + 0.587*float64(g>>8) + 0.114*float64(b>>8)
            histogram[int(gray)]++
        }
    }
    return histogram
}

// 直方图均衡化
func histogramEqualization(img image.Image) image.Image {
    bounds := img.Bounds()
    dst := image.NewRGBA(bounds)

    // 计算直方图
    hist := computeHistogram(img)

    // 计算累积分布函数
    cdf := make([]int, 256)
    cdf[0] = hist[0]
    for i := 1; i < 256; i++ {
        cdf[i] = cdf[i-1] + hist[i]
    }

    // 找到第一个非零 CDF 值
    var cdfMin int
    for i := 0; i < 256; i++ {
        if cdf[i] != 0 {
            cdfMin = cdf[i]
            break
        }
    }

    totalPixels := bounds.Dx() * bounds.Dy()

    // 应用直方图均衡化
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, a := img.At(x, y).RGBA()
            gray := 0.299*float64(r>>8) + 0.587*float64(g>>8) + 0.114*float64(b>>8)

            newGray := uint8(((cdf[int(gray)] - cdfMin) * 255) / (totalPixels - cdfMin))

            ratio := float64(newGray) / float64(gray+1)
            newR := uint8(clamp(float64(r>>8)*ratio, 0, 255))
            newG := uint8(clamp(float64(g>>8)*ratio, 0, 255))
            newB := uint8(clamp(float64(b>>8)*ratio, 0, 255))

            dst.Set(x, y, color.RGBA{newR, newG, newB, uint8(a >> 8)})
        }
    }
    return dst
}
阈值处理示例
// 简单阈值处理
func binarize(img image.Image, threshold uint8) image.Image {
    bounds := img.Bounds()
    dst := image.NewGray(bounds)

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, _ := img.At(x, y).RGBA()
            gray := 0.299*float64(r>>8) + 0.587*float64(g>>8) + 0.114*float64(b>>8)

            var newValue uint8
            if uint8(gray) > threshold {
                newValue = 255
            } else {
                newValue = 0
            }
            dst.SetGray(x, y, color.Gray{newValue})
        }
    }
    return dst
}

// Otsu 自动阈值
func otsuThreshold(img image.Image) uint8 {
    bounds := img.Bounds()
    hist := computeHistogram(img)
    total := bounds.Dx() * bounds.Dy()

    var sum int64
    for i := 0; i < 256; i++ {
        sum += int64(i) * int64(hist[i])
    }

    var maxVariance float64
    var threshold uint8
    var sumB int64
    var wB, wF int

    for t := 0; t < 256; t++ {
        wB += hist[t]
        if wB == 0 {
            continue
        }
        wF = total - wB
        if wF == 0 {
            break
        }

        sumB += int64(t) * int64(hist[t])
        mB := float64(sumB) / float64(wB)
        mF := float64(sum-sumB) / float64(wF)

        variance := float64(wB) * float64(wF) * (mB - mF) * (mB - mF)
        if variance > maxVariance {
            maxVariance = variance
            threshold = uint8(t)
        }
    }
    return threshold
}
图像增强示例
// 调整亮度
func adjustBrightness(img image.Image, factor float64) image.Image {
    bounds := img.Bounds()
    dst := image.NewRGBA(bounds)

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, a := img.At(x, y).RGBA()
            newR := uint8(clamp(float64(r>>8)*factor, 0, 255))
            newG := uint8(clamp(float64(g>>8)*factor, 0, 255))
            newB := uint8(clamp(float64(b>>8)*factor, 0, 255))
            dst.Set(x, y, color.RGBA{newR, newG, newB, uint8(a >> 8)})
        }
    }
    return dst
}

// 调整对比度
func adjustContrast(img image.Image, factor float64) image.Image {
    bounds := img.Bounds()
    dst := image.NewRGBA(bounds)
    adjust := (factor - 1.0) * 128.0

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, a := img.At(x, y).RGBA()
            newR := uint8(clamp(float64(r>>8)+adjust, 0, 255))
            newG := uint8(clamp(float64(g>>8)+adjust, 0, 255))
            newB := uint8(clamp(float64(b>>8)+adjust, 0, 255))
            dst.Set(x, y, color.RGBA{newR, newG, newB, uint8(a >> 8)})
        }
    }
    return dst
}

// 图像反转(负片效果)
func invertColors(img image.Image) image.Image {
    bounds := img.Bounds()
    dst := image.NewRGBA(bounds)

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, a := img.At(x, y).RGBA()
            dst.Set(x, y, color.RGBA{
                255 - uint8(r>>8),
                255 - uint8(g>>8),
                255 - uint8(b>>8),
                uint8(a >> 8),
            })
        }
    }
    return dst
}
轮廓检测示例
// 简单的轮廓检测(基于边缘)
func findContours(img image.Image) [][]image.Point {
    gray := rgbToGrayscale(img)
    bounds := gray.Bounds()
    width, height := bounds.Dx(), bounds.Dy()

    // 二值化
    binary := binarize(gray, 128)

    // 简单的轮廓追踪
    var contours [][]image.Point
    visited := make(map[image.Point]bool)

    for y := 0; y < height; y++ {
        for x := 0; x < width; x++ {
            p := image.Point{x, y}
            if visited[p] {
                continue
            }

            pixel := color.GrayModel.Convert(binary.At(x, y)).(color.Gray)
            if pixel.Y == 255 {
                // 开始一个新的轮廓
                contour := []image.Point{p}
                visited[p] = true

                // 简单的 8 邻域搜索
                neighbors := []image.Point{
                    {x - 1, y}, {x + 1, y},
                    {x, y - 1}, {x, y + 1},
                    {x - 1, y - 1}, {x - 1, y + 1},
                    {x + 1, y - 1}, {x + 1, y + 1},
                }

                for _, n := range neighbors {
                    if n.X >= 0 && n.X < width && n.Y >= 0 && n.Y < height {
                        np := color.GrayModel.Convert(binary.At(n.X, n.Y)).(color.Gray)
                        if np.Y == 255 && !visited[n] {
                            contour = append(contour, n)
                            visited[n] = true
                        }
                    }
                }

                if len(contour) > 4 {
                    contours = append(contours, contour)
                }
            }
        }
    }
    return contours
}
完整示例:综合图像处理流程
func imageProcessingPipeline(inputPath, outputPath string) error {
    // 1. 读取图像
    img, err := loadImage(inputPath)
    if err != nil {
        return err
    }

    // 2. 转换为灰度图
    gray := rgbToGrayscale(img)

    // 3. 应用高斯滤波去噪
    denoised := convolve(gray, gaussianKernel)

    // 4. 直方图均衡化增强对比度
    equalized := histogramEqualization(denoised)

    // 5. 边缘检测
    edges := edgeDetection(equalized)["sobel"]

    // 6. 形态学操作 - 闭运算填充小孔洞
    closed := closing(edges, 3)

    // 7. 保存结果
    outFiles := map[string]image.Image{
        "gray":      gray,
        "denoised":  denoised,
        "equalized": equalized,
        "edges":     edges,
        "closed":    closed,
    }

    for name, result := range outFiles {
        outFile := fmt.Sprintf("%s_%s.jpg", outputPath, name)
        out, err := os.Create(outFile)
        if err != nil {
            return err
        }
        defer out.Close()
        jpeg.Encode(out, result, &jpeg.Options{Quality: 90})
    }

    return nil
}

func main() {
    fmt.Println("开始 Golang 图像处理示例演示...")

    // 执行所有示例
    BasicImageOperations()
    GeometricTransformations()
    FilteringAlgorithms()
    ColorSpaceConversions()
    MorphologicalOperations()
    HistogramProcessing()
    AdvancedTechniques()

    // 运行完整处理流程
    err := imageProcessingPipeline("sample.jpg", "output")
    if err != nil {
        fmt.Printf("处理流程错误:%v\n", err)
    }

    fmt.Println("\nGolang 图像处理示例演示完成!")
}