【发布时间】:2022-11-01 22:31:02
【问题描述】:
我制作了这个读取 PNG 文件的小程序,并找到图像中任何像素的坐标,它是边界所描绘的形状(下面的代码)。
我们在这里定义一个“边框的像素部分”作为“每个在顶部、底部或一侧有一个白色像素的彩色像素”.
这个简单的算法找到形状的边界并忽略构成形状的任何空(白色)像素和任何非空(彩色)像素填充.
我得到的是Point 的切片,所有这些像素的坐标都打印在屏幕上,但它们自然地由找到它们的扫描过程排序,从上到下,从左到右。
我想要实现的是对边界点进行排序,就好像它们在形状周围绘制一条连续线一样,从遇到的第一个边界像素开始,然后围绕形状按顺时针顺序排列。
所以对于这样的正方形(抱歉是一个很小的图像):
8x8 像素,在 10x10 像素的网格中,我得到这个坐标:
2,2
3,2
4,2
5,2
6,2
7,2
2,3
7,3
2,4
7,4
2,5
7,5
2,6
7,6
2,7
3,7
4,7
5,7
6,7
7,7
或者,以图形方式
但我真正想要的是这个(我已经手动排序了这一点):
2,2
3,2
4,2
5,2
6,2
7,2
7,3
7,4
7,5
7,6
7,7
6,7
5,7
4,7
3,7
2,7
2,6
2,5
2,4
2,3
或者,在这个小H形的情况下
我明白了:
2,2
3,2
6,2
7,2
2,3
3,3
6,3
7,3
2,4
4,4
5,4
7,4
2,5
4,5
5,5
7,5
2,6
3,6
6,6
7,6
2,7
3,7
6,7
7,7
但我想要这个(也手动排序):
2,2
3,2
3,3
4,4
5,4
6,3
6,2
7,2
7,3
7,4
7,5
7,6
7,7
6,7
6,6
5,5
4,5
3,6
3,7
2,7
2,6
2,5
2,4
2,3
我希望能很好地说明我的情况。我试图自己考虑一个解决方案,但我不知道如何解决这样的问题。如果您有直接的解决方案或想为我指出正确的方向,或阅读有关此类问题及其解决方案的一些材料,我将非常感激。
对不起,我的英语不好。
这是我的代码:
package main
import (
"fmt"
"image"
"image/png"
"io"
"os"
)
var (
println = fmt.Println
printf = fmt.Printf
)
type Pixel struct {
R int
G int
B int
}
type Point struct {
X int
Y int
}
func main() {
imageFilename := "square1.png"
image.RegisterFormat("png", "png", png.Decode, png.DecodeConfig)
file, err := os.Open(imageFilename)
if err != nil {
println("Error: File could not be opened")
os.Exit(1)
}
defer file.Close()
pixels, err := getPixels(file)
if err != nil {
println("Error: Image could not be decoded")
os.Exit(1)
}
borders := findBorders(pixels)
// Sorting the borders points in clockwise order starting from the first encountered point (but how?)
// borders = sortBorders(borders)
// Print borders points
for _, point := range borders {
printf("%d,%d\n", point.X, point.Y)
}
}
func findBorders(pixels [][]Pixel) []Point {
var borders []Point
for y := 0; y < len(pixels); y++ {
for x := 0; x < len(pixels[y]); x++ {
pixel := pixels[y][x]
if !whitePixel(pixel) {
if whitePixel(pixels[y-1][x]) ||
whitePixel(pixels[y][x-1]) ||
whitePixel(pixels[y+1][x]) ||
whitePixel(pixels[y][x+1]) {
borders = append(borders, Point{x, y})
}
}
}
}
return borders
}
// Return true if a pixel is white
func whitePixel(pixel Pixel) bool {
return pixel.R == 255 && pixel.G == 255 && pixel.B == 255
}
// Get the bi-dimensional pixel array
func getPixels(file io.Reader) ([][]Pixel, error) {
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
var pixels [][]Pixel
for y := 0; y < height; y++ {
var row []Pixel
for x := 0; x < width; x++ {
row = append(row, rgbaToPixel(img.At(x, y).RGBA()))
}
pixels = append(pixels, row)
}
return pixels, nil
}
// img.At(x, y).RGBA() returns four uint32 values, we need something a little more comfortable
func rgbaToPixel(r uint32, g uint32, b uint32, a uint32) Pixel {
return Pixel{int(r / 257), int(g / 257), int(b / 257)}
}
编辑
我有几乎找到了解决办法。对于方形示例,如果在角落 {7, 7} 和 {2, 7} 处失败,因为在我看来,当它在垂直或水平邻域之前找到对角邻域时,如果它在 unsorted 切片中首先出现,则此算法认为它足够好并且跳过最接近的人,仍然被忽略。请看一下:
func sortBorders(unsorted []Point) []Point {
original := make([]Point, len(unsorted))
copy(original, unsorted)
expected := []Point{{2, 2}, {3, 2}, {4, 2}, {5, 2}, {6, 2}, {7, 2}, {7, 3}, {7, 4}, {7, 5}, {7, 6}, {7, 7}, {6, 7}, {5, 7}, {4, 7}, {3, 7}, {2, 7}, {2, 6}, {2, 5}, {2, 4}, {2, 3}}
// Finding the first one is easy
sorted := []Point{unsorted[0]}
unsorted = unsorted[1:]
stillUnsorted := func() bool {
return len(unsorted) > 0
}
lastSorted := func() Point {
return sorted[len(sorted)-1]
}
neighbor := func(s, u Point) bool {
if (s.X == u.X || s.X == u.X+1 || s.X == u.X-1) && (s.Y == u.Y || s.Y == u.Y+1 || s.Y == u.Y-1) {
return true
}
return false
}
removeFromUnsorted := func(index int) {
fresh := unsorted[:index]
unsorted = append(fresh, unsorted[index+1:]...)
}
addToSorted := func(point Point) {
sorted = append(sorted, point)
}
for stillUnsorted() {
for i, point := range unsorted {
if neighbor(lastSorted(), point) {
removeFromUnsorted(i)
addToSorted(point)
println("---------")
println("original:", original)
println("unsorted:", unsorted)
println("sorted :", sorted)
println("expected:", expected)
break
}
}
}
return sorted
}
对于上面的方形示例,这将产生以下输出:
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {2 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {2 6} {2 7} {3 7} {4 7} {5 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {2 6} {2 7} {3 7} {4 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7} {5 7}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {2 6} {2 7} {3 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7} {5 7} {4 7}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {2 6} {2 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7} {5 7} {4 7} {3 7}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 5} {2 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7} {5 7} {4 7} {3 7} {2 6}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 4} {2 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7} {5 7} {4 7} {3 7} {2 6} {2 5}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 3} {2 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7} {5 7} {4 7} {3 7} {2 6} {2 5} {2 4}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
---------
original: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {2 3} {7 3} {2 4} {7 4} {2 5} {7 5} {2 6} {7 6} {2 7} {3 7} {4 7} {5 7} {6 7} {7 7}]
unsorted: [{2 7} {7 7}]
sorted : [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {6 7} {5 7} {4 7} {3 7} {2 6} {2 5} {2 4} {2 3}]
expected: [{2 2} {3 2} {4 2} {5 2} {6 2} {7 2} {7 3} {7 4} {7 5} {7 6} {7 7} {6 7} {5 7} {4 7} {3 7} {2 7} {2 6} {2 5} {2 4} {2 3}]
^Csignal: interrupt
由于 unsorted 切片永远不会达到空状态,因此执行将永远继续......
我怎样才能使这项工作?另外,如何在代码中更清楚地说明这一点?
【问题讨论】:
-
选择一个起点(例如,找到 min x - 如果大于 1 - 找到 min x & y);找到它的邻居(会有 2 个 - 所以选择 1 个);找到没有被使用的邻居(应该是1);继续直到所有点都用完。
-
谢谢@colm.anseo,但这对我来说不是很清楚,我会考虑的
-
@colm.anseo 根据您的意见,我尝试制作一种几乎可以工作的算法。请问还有什么建议吗?
-
如果它跳过角落邻居,请确保首先检查更近的邻居(上、下、左、右),如果没有,然后尝试对角线邻居。
标签: sorting go image-processing pixel