环境准备
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 图像处理示例演示完成!")
}