Go 将接口断言为其类型

Go 将接口断言为其类型,go,assertion,go-interface,Go,Assertion,Go Interface,在一般情况下,我无法将图像的像素作为数组优雅地获取 f, err := os.Open(imgPath) check(err) defer f.Close() img, _, err := image.Decode(bufio.NewReader(f)) check(err) pixels, err := getPixels(img) check(err) // Logic with pixels. 现在函数getPixels如下所示: func getPixels(img image.Ima

在一般情况下,我无法将图像的像素作为数组优雅地获取

f, err := os.Open(imgPath)
check(err)
defer f.Close()
img, _, err := image.Decode(bufio.NewReader(f))
check(err)
pixels, err := getPixels(img)
check(err)
// Logic with pixels.
现在函数getPixels如下所示:

func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(*image.NRGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.CMYK); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.NRGBA64); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Paletted); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA64); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(eval(fmt.Sprintf("%T", img))); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
func getPixels(img image.Image) ([]uint8, error) {
    switch i := img.(type) {
    case *image.NRGBA:
        return i.Pix, nil
    case *image.Alpha:
        return i.Pix, nil
    case *image.Alpha16:
        return i.Pix, nil
    case *image.CMYK:
        return i.Pix, nil
        // ...
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
但我觉得这很难看。Golang知道图像的类型,我更喜欢这样:

func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(*image.NRGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.CMYK); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.NRGBA64); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Paletted); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA64); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(eval(fmt.Sprintf("%T", img))); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
func getPixels(img image.Image) ([]uint8, error) {
    switch i := img.(type) {
    case *image.NRGBA:
        return i.Pix, nil
    case *image.Alpha:
        return i.Pix, nil
    case *image.Alpha16:
        return i.Pix, nil
    case *image.CMYK:
        return i.Pix, nil
        // ...
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}

我也不能断言
reflect.TypeOf(img)
。也许有一种方法可以从
reflect.type
界面获取类型?

如果。。。else结构可以通过使用以下方法简化:

func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(*image.NRGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.CMYK); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.NRGBA64); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Paletted); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA64); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(eval(fmt.Sprintf("%T", img))); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
func getPixels(img image.Image) ([]uint8, error) {
    switch i := img.(type) {
    case *image.NRGBA:
        return i.Pix, nil
    case *image.Alpha:
        return i.Pix, nil
    case *image.Alpha16:
        return i.Pix, nil
    case *image.CMYK:
        return i.Pix, nil
        // ...
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}
你仍然需要列出所有可能的类型,但这样更好

由于所有图像实现都是结构指针,具有名为
Pix
的字段,因此可以使用反射来获取该字段。此实现将在不做任何更改的情况下处理将来的映像实现(如果它们也是带有
Pix
字段的结构)

这就是它的样子:

func getPix(img image.Image) ([]uint8, error) {
    v := reflect.ValueOf(img)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    if v.Kind() == reflect.Struct {
        pv := v.FieldByName("Pix")
        if pv.IsValid() {
            if pix, ok := pv.Interface().([]uint8); ok {
                return pix, nil
            }
        }
    }

    return nil, fmt.Errorf("unknown image type %T", img)
}
测试它:

fmt.Println(getPix(&image.NRGBA{}))
fmt.Println(getPix(&image.RGBA{}))

type unknownImage struct{ image.Image }
fmt.Println(getPix(unknownImage{}))
输出(在上尝试):

[]
[] 
[]未知映像类型main.unknownImage

是的,
开关
从我的脑海中消失了。开关看起来好一点。但在golang中是否可以将接口断言为其类型(“%T”)?一般情况下,可能有几十种不同的类型。@Andrey Go是一种静态类型语言。您只能将类型assert指定给具体类型或其他接口类型,但该类型必须在编译时已知。对反射使用第二种方法。您试图对
Pix
字段进行何种处理?我很好奇,因为尽管不同图像类型的
Pix
字段的类型是
[]uint8
,但切片的实际内容并不一致。例如,
CMYK
图像类型在切片中为每个像素逐个存储C、M、Y和K的值,但是
Alpha
仅为每个像素存储一个值。这就是为什么
image.image
接口有一个
At()
方法返回任何给定像素的
color.color
值。然后我计算平均颜色。使用
img.At(x,y).RGBA()
获取每个像素的颜色要比在分辨率为3840x1200的图像上处理
[]uint8
:~530ms vs~315ms慢。我处理很多if图像,所以我决定使用img.Pix。但是带有库“枕头”的Python速度更快:~215ms:)。您需要这种性能提升吗?如果是,您确定在不考虑图像类型的情况下得到了正确的结果吗?特别是对于具有多个组件的图像类型(例如CMYK、NRGBA64等),我假设您需要计算每个组件的平均值。我可以不进行优化就完成它,但为什么不好玩呢。图像为png、jpeg或tiff、彩色(4通道)或灰色(1通道)。在imagemagick的帮助下检查结果。