如何使用Golang对图片进行边缘增强和形状识别
1. 前言
图像处理成为应用广泛的领域,而边缘增强和形状识别是其中的重要操作。本文将介绍如何使用Golang进行边缘增强和形状识别的基本操作。
2. 边缘增强
边缘增强是指通过对图像边缘处进行增强,从而突出图像中重要的信息,提高图像的视觉效果。下面将介绍如何使用Golang对图像进行边缘增强。
2.1 导入相关库
Golang支持对图像进行处理的库包括image和image/color两个包。其中,image/color包提供了一些常用的颜色模型和对应的转换函数;而image包则提供了对图像进行读取、写入、处理等操作的接口和函数。
import (
"image"
"image/color"
)
2.2 读取图像
使用image包中的函数,我们可以轻松地读取一张图像。
// 读取图片
img, _, err := image.Decode(file)
if err != nil {
panic(err)
}
2.3 创建边缘增强处理器
使用灰度图像能够更准确地识别图像边缘,因此我们需要将彩色图像转换为灰度图像。在这里我们选择使用image/color中的RGBToYCbCr()函数进行转换。转换后的灰度图像只有一个亮度通道,在下一步操作中我们将对该通道进行处理。
对灰度图像进行边缘增强处理时,我们可以使用Sobel算子来进行卷积。Sobel算子是一种常用的边缘检测算子,可以将图像中的明显边缘区分出来。
// 转换为灰度图像
gray := image.NewGray(img.Bounds())
for x := 0; x < img.Bounds().Max.X; x++ {
for y := 0; y < img.Bounds().Max.Y; y++ {
c := color.RGBToYCbCr(img.At(x, y).(color.RGBA))
gray.Set(x, y, color.Gray{c.Y})
}
}
// 创建Sobel算子
sobelX := [3][3]int{{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}}
sobelY := [3][3]int{{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}}
// 创建边缘增强处理器
eh := edgeEnhancer{gray, sobelX, sobelY, 50}
edges := eh.process()
2.4 边缘增强处理
使用创建好的边缘增强处理器,我们可以对灰度图像进行边缘增强处理。此处使用的边缘增强方法为Sobel算子,通过对图像进行卷积操作来突出图像中的明显边缘。
type edgeEnhancer struct {
gray *image.Gray
sx, sy [3][3]int
thresh int
}
// 边缘增强处理
func (eh *edgeEnhancer) process() *image.Gray {
dst := image.NewGray(eh.gray.Bounds())
for y := 1; y < eh.gray.Bounds().Max.Y-1; y++ {
for x := 1; x < eh.gray.Bounds().Max.X-1; x++ {
gx, gy := 0, 0
for j := -1; j <= 1; j++ {
for i := -1; i <= 1; i++ {
gx += eh.sx[j+1][i+1] * int(eh.gray.GrayAt(x+i, y+j).Y)
gy += eh.sy[j+1][i+1] * int(eh.gray.GrayAt(x+i, y+j).Y)
}
}
g := int(math.Sqrt(float64(gx*gx + gy*gy)))
if g >= eh.thresh {
dst.SetGray(x, y, color.Gray{uint8(g)})
} else {
dst.SetGray(x, y, color.Gray{0})
}
}
}
return dst
}
3. 形状识别
形状识别是指对图像中的形状进行识别、分析、处理等操作。下面将介绍如何使用Golang对图像进行形状识别。
3.1 导入相关库
形状识别需要使用到的库包括image、image/color和math/bits三个包。其中,bits包提供了一些与二进制位操作相关的函数,而image/color和image两个包仍然用于图像处理。
import (
"image"
"image/color"
"math/bits"
)
3.2 读取图像并转换为二值图像
形状识别需要将图像转换为二值图像。通常,对彩色图像的转换方式有概率阈值法、大津法、灰度共生矩阵法、OTSU算法等,这里我们选择使用OTSU算法进行二值化处理。
type binarizer struct {
gray *image.Gray
thresh int
}
// 二值化处理
func (bz *binarizer) binarize() *image.Gray {
dst := image.NewGray(bz.gray.Bounds())
hist := make([]int, 256)
for y := 0; y < bz.gray.Bounds().Max.Y; y++ {
for x := 0; x < bz.gray.Bounds().Max.X; x++ {
c := bz.gray.GrayAt(x, y).Y
hist[c]++
}
}
sum, sumB, wB, wF, varMax, threshold := 0, 0, 0, 0, 0.0, 0
total := bz.gray.Bounds().Max.X * bz.gray.Bounds().Max.Y
for i := 0; i < 256; i++ {
sum += i * hist[i]
}
for t := 0; t < 256; t++ {
wB += hist[t]
if wB == 0 {
continue
}
wF = total - wB
if wF == 0 {
break
}
sumB += t * hist[t]
mB := float64(sumB) / float64(wB)
mF := float64(sum-sumB) / float64(wF)
varBetween := float64(wB) * float64(wF) * (mB - mF) * (mB - mF)
if varBetween > varMax {
varMax = varBetween
threshold = t
}
}
bz.thresh = threshold
for y := 0; y < bz.gray.Bounds().Max.Y; y++ {
for x := 0; x < bz.gray.Bounds().Max.X; x++ {
c := bz.gray.GrayAt(x, y).Y
if int(c) >= threshold {
dst.SetGray(x, y, color.Gray{255})
} else {
dst.SetGray(x, y, color.Gray{0})
}
}
}
return dst
}
3.3 创建形状识别处理器
对二值图像进行形状识别的常用算法是Hough变换。Hough变换可以将图像中的曲线或直线的参数转换成二维空间内的点对,从而使得在空间内对这些曲线或直线进行检测和识别成为可能。下面我们先要对二值图像进行Hough变换,创建好处理器后便可进行处理。
type shapeRecognizer struct {
bin *image.Gray
hough [][]int
maxRho int
maxTh int
minTh int
weights []int
thresh int
threshP int
}
// 创建形状识别器
func newShapeRecognizer(bin *image.Gray, maxTh, minTh int) *shapeRecognizer {
sr := &shapeRecognizer{
bin: bin,
maxRho: int(math.Sqrt(float64(bin.Bounds().Max.X*bin.Bounds().Max.X + bin.Bounds().Max.Y*bin.Bounds().Max.Y))),
maxTh: maxTh,
minTh: minTh,
thresh: 200,
threshP: 50,
}
sr.computeHough()
sr.computeWeights()
return sr
}
3.4 Hough变换处理
对二值图像进行Hough变换,我们需要先定义出Hough空间中的x、y坐标和极径rho(由于需要遍历整个空间,因此rho的最大值通常需要根据图像大小来确定。此处由于处理的是正方形的图像,因此rho的最大值为图像的对角线长度。)、极角theta等参数,再通过对图像中的每个像素进行遍历,并计算出它们在Hough空间中的坐标。
// 计算Hough变换
func (sr *shapeRecognizer) computeHough() {
w, h := sr.bin.Bounds().Max.X, sr.bin.Bounds().Max.Y
sr.hough = make([][]int, sr.maxRho+1)
for i := 0; i < len(sr.hough); i++ {
sr.hough[i] = make([]int, sr.maxTh+1)
}
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
if sr.bin.GrayAt(x, y).Y == 0 {
continue
}
for th := sr.minTh; th <= sr.maxTh; th++ {
r := int(float64(x)*math.Cos(float64(th)*math.Pi/180) + float64(y)*math.Sin(float64(th)*math.Pi/180))
if r >= 0 && r <= sr.maxRho {
sr.hough[r][th]++
}
}
}
}
}
3.5 计算权重
通过计算每个Hough空间中的坐标点的权重,我们可以将多个点密集的区域(即形状)从背景中提取出来。
// 计算权重
func (sr *shapeRecognizer) computeWeights() {
sr.weights = make([]int, sr.maxTh+1)
for r := 0; r < sr.maxRho; r++ {
for th := sr.minTh; th <= sr.maxTh; th++ {
if sr.hough[r][th] > sr.thresh {
for dth := -sr.threshP; dth <= sr.threshP; dth++ {
idx := sr.adjustTheta(th+dth, sr.maxTh)
sr.weights[idx] += sr.hough[r][th]
}
}
}
}
}
3.6 形状识别处理
通过定义识别器的参数后,我们可以对提取出的形状区域进行处理,检测其中是否存在目标形状,从而进行进一步的处理。
// 形状识别处理
func (sr *shapeRecognizer) process() []image.Point {
points := make([]image.Point, 0)
for th := sr.minTh; th <= sr.maxTh; th++ {
if sr.weights[th] > sr.thresh {
for r := 0; r < sr.maxRho; r++ {
if sr.hough[r][th] > sr.thresh {
xmin, ymin := 0, 0
xmax, ymax := sr.bin.Bounds().Max.X-1, sr.bin.Bounds().Max.Y-1
if th >= 45 && th < 135 {
xmin = int(float64(r)/math.Cos(float64(th)*math.Pi/180) - float64(ymax)*math.Tan(float64(th)*math.Pi/180))
if xmin < 0 {
xmin = 0
}
xmax = int(float64(r)/math.Cos(float64(th)*math.Pi/180) - float64(ymin)*math.Tan(float64(th)*math.Pi/180))
if xmax >= sr.bin.Bounds().Max.X {
xmax = sr.bin.Bounds().Max.X - 1
}
} else {
ymin = int(float64(r)/math.Sin(float64(th)*math.Pi/180) - float64(xmax)*math.Tan(float64(th)*math.Pi/180))
if ymin < 0 {
ymin = 0
}
ymax = int(float64(r)/math.Sin(float64(th)*math.Pi/180) - float64(xmin)*math.Tan(float64(th)*math.Pi/180))
if ymax >= sr.bin.Bounds().Max.Y {
ymax = sr.bin.Bounds().Max.Y - 1
}
}
for y := ymin; y <= ymax; y++ {
for x := xmin; x <= xmax; x++ {
if sr.bin.GrayAt(x, y).Y > 0 {
points = append(points, image.Pt(x, y))
}
}
}
}
}
}
}
return points
}
// 调整极角
func (sr *shapeRecognizer) adjustTheta(th, maxTh int) int {
if th < 0 {
th += maxTh
} else if th > maxTh {
th -= maxTh
}
return th
}
4. 总结
本文介绍了如何使用Golang对图像进行边缘增强和形状识别的基本操作。使用Golang进行图像处理可以通过调用相应的库包,使得处理过程更加简单明了、代码更加清晰易读。在实际应用中,这些基本操作也是图像处理的基础,对于进一步的高级操作和算法实现有着很重要的意义。