golang中的图像image处理详解

news2024/11/19 16:37:39

常用的图像格式有png,jpeg,gif,对应的文件后缀为png,jpg,gif,当然文件的具体内容编码才能证明存放的是哪种文件,图像文件的头部都存储了具体标志,比如jpeg前缀\xffd8,png前缀\x89PNG\r\n\x1a\n,gif前缀GIF8?a,使用文本编辑器打开图像文件就能明显看到。

读取jpg文件头部

f, err := os.Open("./image1.jpg")
if err != nil {
    panic(err)
}
defer f.Close()

r := bufio.NewReader(f)
b, _ := r.Peek(2)
fmt.Println(b) // [255 216] 就是 \xffd8

因此在解析之前先要设置好前缀信息,比如

import _ "image/jpeg"
import _ "image/png"

查看其init方法

func init() {
	image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig)
}

通过对比这个magic前缀来决定使用哪个编码解码方式。而name参数只是名称。

var pic = "./image1.jpg"

func main() {
	f, err := os.Open(pic)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	img, fmtName, err := image.Decode(f)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Name: %v, Bounds: %v, Color: %+v", fmtName, img.Bounds(), img.ColorModel())
}

关于Image接口

type Image interface {
  ColorModel() color.Model // 返回图片的颜色模型
  Bounds() Rectangle       // 返回图片外框边界
  At(x, y int) color.Color // 返回(x,y)像素点的颜色
}

也就是图像的宽高,以及各个像素点的颜色值。

// 透明度
func NewAlpha(r Rectangle) *Alpha
func NewAlpha16(r Rectangle) *Alpha16

func NewRGBA(r Rectangle) *RGBA
func NewRGBA64(r Rectangle) *RGBA64

func NewNRGBA(r Rectangle) *NRGBA
func NewNRGBA64(r Rectangle) *NRGBA64

func NewGray(r Rectangle) *Gray
func NewGray16(r Rectangle) *Gray16

func NewCMYK(r Rectangle) *CMYK

func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr

func NewNYCbCrA(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *NYCbCrA

func NewPaletted(r Rectangle, p color.Palette) *Paletted

func NewUniform(c color.Color) *Uniform

RGBA

RGBA represents a traditional 32-bit alpha-premultiplied color, having 8 bits for each of red, green, 
blue and alpha.
// RGBA is an in-memory image whose At method returns color.RGBA values.
type RGBA struct {
	// Pix holds the image's pixels, in R, G, B, A order. The pixel at
	// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
	Pix []uint8
	// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
	Stride int
	// Rect is the image's bounds.
	Rect Rectangle
}

// NewRGBA returns a new RGBA image with the given bounds.
func NewRGBA(r Rectangle) *RGBA {
	return &RGBA{
		Pix:    make([]uint8, pixelBufferLength(4, r, "RGBA")),
		Stride: 4 * r.Dx(),
		Rect:   r,
	}
}

type Rectangle struct {
	Min, Max Point
}

RGBA对象实现了 Image 接口,用来操作带有RGB颜色以及透明度A的图像,因此每一个像素点有四个值,都是uint8类型,存储在切片 Pix 中,像素点是矩阵排列,因此需要规则转换。

其中r.Dx()为图像的宽度,即X轴最大值。

那么对于像素点(x,y),我们要怎么找到其在 Pix 切片中的起始位置呢,算法在注释中已经给明了,为了便于理解就假设Rect.Min在(0,0)位置,一个像素点占四个位置,然后就很容易得出结果 Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4]

Stride 的注释是像素点在垂直方向上的间距,在这里它是4倍的图像宽度,方便在y轴上计算用的。

提供的方法

func (p *RGBA) ColorModel() color.Model // 返回颜色模型
func (p *RGBA) Bounds() Rectangle // 返回图像边界
func (p *RGBA) At(x, y int) color.Color // 返回制定像素点的颜色
func (p *RGBA) RGBAAt(x, y int) color.RGBA // 返回制定像素点的颜色,返回color.RGBA
func (p *RGBA) RGBA64At(x, y int) color.RGBA64 // 返回制定像素点的颜色,返回color.RGBA64
func (p *RGBA) Set(x, y int, c color.Color) // 设置制定像素点的颜色
func (p *RGBA) SetRGBA64(x, y int, c color.RGBA64) // 设置制定像素点的颜色
func (p *RGBA) SetRGBA(x, y int, c color.RGBA) // 设置制定像素点的颜色
func (p *RGBA) SubImage(r Rectangle) Image // 返回制定区域的Image,注意他们会共享Pix切片
func (p *RGBA) Opaque() bool // 判断是否每个像素点的A值都是255,即是否完全不透明
func NewRGBA(r Rectangle) *RGBA

RGBA64

RGBA64 represents a 64-bit alpha-premultiplied color, having 16 bits for each of red, green, blue and 
alpha.

前面的 RGBA 的一个像素点通过四个 uint8 来存储,而 RGBA64 的一个像素点则需要八个 uint8 来存储,也就是64位,R,G,B,A都是两个字节存储。这使得颜色表现更细腻一些。

NRGBA

NRGBA represents a non-alpha-premultiplied 32-bit color.

颜色编码没有alpha-premultiplied

NRGBA64

NRGBA64 represents a non-alpha-premultiplied 64-bit color, having 16 bits for each of red, green, blue 
and alpha.

Alpha

Alpha represents an 8-bit alpha color.

透明度 0-255,0完全透明,255完全不透明,也可以说白表示完全不透明,黑表示完全透明,灰则是半透明。

支持alpha通道的格式有png, tag, tif。

dx := 500
dy := 500
img := image.NewAlpha(image.Rect(0, 0, dx, dy))
for i := 0; i < dx; i++ {
   for j := 0; j < dy; j++ {
      img.Set(i, j, color.Alpha{A: uint8(i % 256)})
   }
}

f, _ := os.Create("./image3.png")
defer f.Close()

b := bufio.NewWriter(f)
png.Encode(b, img)
b.Flush()

在这里插入图片描述

如果使用jpg来存储的话

dx := 500
dy := 500
img := image.NewAlpha(image.Rect(0, 0, dx, dy))
for i := 0; i < dx; i++ {
   for j := 0; j < dy; j++ {
      img.Set(i, j, color.Alpha{A: uint8(i % 256)})
   }
}

f, _ := os.Create("./image3.jpg")
defer f.Close()

b := bufio.NewWriter(f)
jpeg.Encode(b, img, nil)
b.Flush()

在这里插入图片描述

那是因为jpg, bmp, gif等图像格式不支持alpha通道,于是需要将 Alpha 颜色转换成 RGBA 颜色,同时会过滤掉A这个值。

func (c Alpha) RGBA() (r, g, b, a uint32) {
   a = uint32(c.A)
   a |= a << 8 // 被放大了 2^8 倍
   return a, a, a, a
}

于是 R=G=B=A,那么,为什么RGB三值相等的时候得到的颜色是黑白灰呢?

那是因为(0,0,0)表示纯黑,(255,255,255)表示纯白,三值相等说明在三个颜色方向上达到了平衡,看到的效果就是黑白灰渐变色。

Options 参数为编码质量,它的取值范围是1-100,值越高质量越好,默认是 75

Alpha16

Alpha16 represents a 16-bit alpha color.

Gray

Gray represents an 8-bit grayscale color.

灰度,指RGB三个颜色值相等的一种情况,正如上面所说,表现出来的就是黑白灰,灰度是完全不透明的。

type Gray struct {
   Y uint8
}

func (c Gray) RGBA() (r, g, b, a uint32) {
   y := uint32(c.Y)
   y |= y << 8
   return y, y, y, 0xffff
}
dx := 500
dy := 500
img := image.NewGray(image.Rect(0, 0, dx, dy))
for i := 0; i < dx; i++ {
   for j := 0; j < dy; j++ {
      img.Set(i, j, color.Gray{Y: uint8(i % 256)})
   }
}

f, _ := os.Create("./image4.jpg")
defer f.Close()

b := bufio.NewWriter(f)
jpeg.Encode(b, img, nil)
b.Flush()

在这里插入图片描述

Gray16

Gray16 represents a 16-bit grayscale color.

CMYK

// CMYK represents a fully opaque CMYK color, having 8 bits for each of cyan,
// magenta, yellow and black.
//
// It is not associated with any particular color profile.
type CMYK struct {
   C, M, Y, K uint8
}

印刷四色模式是彩色印刷时采用的一种套色模式,利用色料的三原色混色原理,加上黑色油墨,共计四种颜色混合叠加,形成所谓"全彩印刷"。四种标准颜色是:C:Cyan = 青色,又称为’天蓝色’或是’湛蓝’;M:Magenta = 品红色,又称为’洋红色’;Y:Yellow = 黄色;K:blacK=黑色,虽然有文献解释说这里的K应该是Key Color(定位套版色),但其实是和制版时所用的定位套版观念混淆而有此一说。此处缩写使用最后一个字母K而非开头的B,是为了避免与Blue混淆。CMYK模式是减色模式,相对应的RGB模式是加色模式。

它和RGB相比有一个很大的不同:RGB模式是一种屏幕显示发光的色彩模式,你在一间黑暗的房间内仍然可以看见屏幕上的内容;

CMYK是一种用于印刷品依靠反光的色彩模式,我们是怎样阅读报纸的内容呢?是由阳光或灯光照射到报纸上,再反射到我们的眼中,才看到内容。它需要有外界光源,如果你在黑暗房间内是无法阅读报纸的。

只要在屏幕上显示的图像,就是RGB模式表现的。只要是在印刷品上看到的图像,就是CMYK模式表现的。比如期刊、杂志、报纸、宣传画等,都是印刷出来的,那么就是CMYK模式的了。

RGB以黑色为底色加,即RGB均为0是黑色,均为255是白色。

CMY以白色为底色减,即CMY均为0是白色,均为100%是黑色(但在实际中,由于油墨的纯度等问题这样得不到纯正的黑色,因此引入K)。

dx := 500
dy := 500
img := image.NewCMYK(image.Rect(0, 0, dx, dy))
for i := 0; i < dx; i++ {
   for j := 0; j < dy; j++ {
      img.Set(i, j, color.CMYK{C: uint8(i % 256), M: uint8(i % 256), Y: uint8(i % 256), K: uint8(i % 256)})
   }
}

f, _ := os.Create("./image5.jpg")
defer f.Close()

b := bufio.NewWriter(f)
jpeg.Encode(b, img, nil)
b.Flush()

在这里插入图片描述

可以看到与RGB颜色正好相反。

所以,如果图像是要印刷出来的话,那么应该采用CMYK颜色值,因为用RGB颜色打印出来的效果会有较大差异。

YCbCr

// YCbCr represents a fully opaque 24-bit Y'CbCr color, having 8 bits each for
// one luma and two chroma components.
//
// JPEG, VP8, the MPEG family and other codecs use this color model. Such
// codecs often use the terms YUV and Y'CbCr interchangeably, but strictly
// speaking, the term YUV applies only to analog video signals, and Y' (luma)
// is Y (luminance) after applying gamma correction.
//
// Conversion between RGB and Y'CbCr is lossy and there are multiple, slightly
// different formulae for converting between the two. This package follows
// the JFIF specification at https://www.w3.org/Graphics/JPEG/jfif3.pdf.
type YCbCr struct {
   Y, Cb, Cr uint8
}

这是另外一种颜色模式。使用亮度(黑白灰),蓝色、红色的偏移量描述图像信号的色彩空间,应用在视频领域。jpeg图像使用的就是YCbCr颜色。

使用YCbCr是因为,人眼对于亮度对比的感知能力比色彩的感知能力要强,把亮度分量分离出来后,可以有针对性地使用不同的量化表、采样因子来达到不同的压缩率,且人眼感知不强。如果只有Y信号分量,那么表示的就是黑白灰度图像。

jpeg图像使用的就是 YCbCr 颜色。

关于RGB, YUV, YCbCr三种颜色空间

NYCbCrA

// NYCbCrA represents a non-alpha-premultiplied Y'CbCr-with-alpha color, having
// 8 bits each for one luma, two chroma and one alpha component.
type NYCbCrA struct {
   YCbCr
   A uint8
}

Paletted

调色板

前面提到的类型,都是直接将像素点的各个分量存储在 Pix 中,而调色板类型,则是先有一个调色板p存储了一些color值,而 Pix 中存储的值是p中的索引,这样像素点的颜色也就对应上了,但是Pix中byteePerPixel为1,也就是索引值最大255,所以调色板里最多有256个color。调色板里的color要先预制好,Set方法只能去里面匹配,如果没有找到则索引为0。

func NewPaletted(r Rectangle, p color.Palette) *Paletted {
   return &Paletted{
      Pix:     make([]uint8, pixelBufferLength(1, r, "Paletted")),
      Stride:  1 * r.Dx(),
      Rect:    r,
      Palette: p,
   }
}

NewPaletted 函数得到的是一个调色板,还没有设置 Pix,因此得到的图像的表现就是由调色板第一个颜色平铺而成的背景。

默认提供的两个调色板 palette.Plan9,palette.WebSafe

如果你的图像的颜色区间不大,那么使用调色板模式就可以大大减少存储空间。

Uniform

// Uniform is an infinite-sized Image of uniform color.
// It implements the color.Color, color.Model, and Image interfaces.
type Uniform struct {
   C color.Color
}

func (c *Uniform) Bounds() Rectangle { 
    return Rectangle{Point{-1e9, -1e9}, Point{1e9, 1e9}} 
}

// NewUniform returns a new Uniform image of the given color.
func NewUniform(c color.Color) *Uniform {
	return &Uniform{c}
}

用同一个color来填充一个无限大的幕布,我们可以从幕布上截取一块,当然,这个功能也好实现,只是官方提供了而已,

dx := 500
dy := 500
r := image.Rect(0, 0, dx, dy)
img := image.NewRGBA(r)
for i := 0; i < dx; i++ {
    for j := 0; j < dy; j++ {
        img.Set(i, j, color.Black)
    }
}

或者

dx := 500
dy := 500
r := image.Rect(0, 0, dx, dy)
img := image.NewRGBA(r)
imgBack := image.NewUniform(image.Black)
draw.Draw(img, r, imgBack, image.Point{}, draw.Src)

gif图像

type GIF struct {
   // 连续的图片
   Image []*image.Paletted 
   // 连续的延迟时间,单位是百分之一秒,delay中数值表示展示的时间,10就表示0.1秒
   Delay []int             
   // LoopCount 控制动画的重复播放规则
   // 0 表示无限循环
   // -1 表示只播放一次
   // 其它的播放 LoopCount+1 次
   LoopCount int
   // Disposal is the successive disposal methods, one per frame. For
   // backwards compatibility, a nil Disposal is valid to pass to EncodeAll,
   // and implies that each frame's disposal method is 0 (no disposal
   // specified).
   Disposal []byte
   // Config is the global color table (palette), width and height. A nil or
   // empty-color.Palette Config.ColorModel means that each frame has its own
   // color table and there is no global color table. Each frame's bounds must
   // be within the rectangle defined by the two points (0, 0) and
   // (Config.Width, Config.Height).
   //
   // For backwards compatibility, a zero-valued Config is valid to pass to
   // EncodeAll, and implies that the overall GIF's width and height equals
   // the first frame's bounds' Rectangle.Max point.
   Config image.Config
   // BackgroundIndex is the background index in the global color table, for
   // use with the DisposalBackground disposal method.
   BackgroundIndex byte
}

gif图像本身可以存储静态图和动态图,但如今该图像主要被用来存储动态图,且在大部分系统上都支持。但是相对于webp这些新式的动态图格式,其在颜色质量和压缩率上的表现相对不如人意。gif图像和一般的图像直接存储图像内容不同,而是通过一个颜色表(调色板)映射来表达对应的图像的内容。也就是说图像中存在一张颜色表存储图像中出现的颜色,然后每一帧的图像通过颜色索引来表示颜色。GIF支持的最大颜色表数量为8bit,即256色,所以一般的动态图能够看到图像中存在明显的颜色梯度变化的效应,也就是不够清晰。

func image14() {
   p1 := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9)
   p2 := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9)

   for y := 0; y < 100; y++ {
      p1.Set(50, y, color.RGBA{R: 0, G: 0, B: 255, A: 255})
   }

   for y := 0; y < 100; y++ {
      p2.Set(y, 50, color.RGBA{R: 255, G: 0, B: 0, A: 255})
   }

   g := &gif.GIF{
      Image:     []*image.Paletted{p1, p2},
      Delay:     []int{10, 100}, // p1展示0.1秒,p2展示1秒
      LoopCount: 0,
   }
   f1, _ := os.Create("./image14.gif")
   defer f1.Close()

   gif.EncodeAll(f1, g)
}

在这里插入图片描述

func image15() {
   p1 := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9)
   p2 := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9)
   p3 := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9)

   m1, _ := os.Open("./1111.jpg")
   defer m1.Close()
   img1, _, _ := image.Decode(m1)

   m2, _ := os.Open("./2222.jpg")
   defer m2.Close()
   img2, _, _ := image.Decode(m2)

   m3, _ := os.Open("./3333.jpg")
   defer m3.Close()
   img3, _, _ := image.Decode(m3)

   draw.Draw(p1, p1.Bounds(), img1, img1.Bounds().Min, draw.Src)
   draw.Draw(p2, p2.Bounds(), img2, img2.Bounds().Min, draw.Src)
   draw.Draw(p3, p3.Bounds(), img3, img3.Bounds().Min, draw.Src)

   g := &gif.GIF{
      Image:     []*image.Paletted{p1, p2, p3},
      Delay:     []int{100, 100, 100},
      LoopCount: 0,
   }
   f1, _ := os.Create("./image15.gif")
   defer f1.Close()

   gif.EncodeAll(f1, g)
}

在这里插入图片描述

图片操作

import "image/draw"

func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)

func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, 
mp image.Point, op Op)

参数说明

dst  要绘制的目标图像
r    在目标图像的哪个区域绘制
src  源图像
sp   start point,从源图像的哪个点开始,范围是 r 的长宽
mask 是绘图时用的蒙版,控制替换图片的方式
op   两个图像采用不同的混合方式得出的结果不同,此处有 Porter-Duff 12 等式,即12种混合方式,参考 
https://blog.csdn.net/ison81/article/details/5468763 和 
http://www.blogjava.net/onedaylover/archive/2008/01/16/175675.html

1、图像合并

func image8() {
   // 蓝色 500*500
   img1 := image.NewRGBA(image.Rect(0, 0, 500, 500))
   for i := 0; i < img1.Bounds().Max.X; i++ {
      for j := 0; j < img1.Bounds().Max.Y; j++ {
         img1.Set(i, j, color.RGBA{B: 255, A: 255})
      }
   }

   // 红色 200*200
   img2 := image.NewRGBA(image.Rect(0, 0, 200, 200))
   for i := 0; i < img2.Bounds().Max.X; i++ {
      for j := 0; j < img2.Bounds().Max.Y; j++ {
         img2.Set(i, j, color.RGBA{R: 255, A: 255})
      }
   }

   draw.Draw(img1, img2.Bounds(), img2, img2.Bounds().Min, draw.Src)

   f, _ := os.Create("./image8.jpg")
   defer f.Close()

   b := bufio.NewWriter(f)
   jpeg.Encode(b, img1, nil)
   b.Flush()
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVbqp6Qr-1675236120865)(D:\dev\php\magook\trunk\server\md\img\image-20230131164352370.png)]

2、图片置灰

使用灰度Gray作为目标即可

func image9() {
   f, err := os.Open("./image1.jpg")
   if err != nil {
      panic(err)
   }
   defer f.Close()

   src, _, _ := image.Decode(f)

   dst := image.NewGray(src.Bounds())

   draw.Draw(dst, src.Bounds(), src, src.Bounds().Min, draw.Src)

   f1, _ := os.Create("./image9.jpg")
   defer f1.Close()

   b := bufio.NewWriter(f1)
   jpeg.Encode(b, dst, nil)
   b.Flush()
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bpzXjqnC-1675236120866)(D:\dev\php\magook\trunk\server\md\img\image-20230131170943418.png)]

3、图片裁剪

func image10() {
   f, err := os.Open("./image1.jpg")
   if err != nil {
      panic(err)
   }
   defer f.Close()

   src, _, _ := image.Decode(f)

   // jpeg 使用的 YCbCr 颜色
   dst := src.(*image.YCbCr).SubImage(image.Rect(0, 0, 500, 500))

   f1, _ := os.Create("./image10.jpg")
   defer f1.Close()

   b := bufio.NewWriter(f1)
   jpeg.Encode(b, dst, nil)
   b.Flush()
}

4、图片缩放

图片的缩小可以理解为只抽取部分像素点,比如等比例缩小为原来的一半,那么就是每间隔一个点抽取一个像素,然后将这些像素点排列在新的画布上,原来的1200*800变成600*400,没有原来清晰了。

func image11() {
   f, err := os.Open("./image1.jpg")
   if err != nil {
      panic(err)
   }
   defer f.Close()

   scale := 2 // 等比例缩小一半

   src, _, _ := image.Decode(f)
   width := src.Bounds().Max.X
   height := src.Bounds().Max.Y

   dstWidth := width / scale
   dstHeight := height / scale

   dst := image.NewRGBA(image.Rect(0, 0, dstWidth, dstHeight))

   for i := 0; i < dstWidth; i++ {
      for j := 0; j < dstHeight; j++ {
         dst.Set(i, j, src.At(i*scale, j*scale))
      }
   }

   f1, _ := os.Create("./image11.jpg")
   defer f1.Close()

   b := bufio.NewWriter(f1)
   jpeg.Encode(b, dst, nil)
   b.Flush()
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DqRJZrJR-1675236120866)(D:\dev\php\magook\trunk\server\md\img\image-20230201094227274.png)]

等比例缩放和非等比例缩放是一样的道理。次方法成为邻域插值法。

缩小操作效果还可以,但是放大操作由于算法的原因会导致一些像素点不太合理,有时候会得到不存在的像素点,因此效果不是太好。

传统差值原理和评价

在传统图像插值算法中,邻插值较简单,容易实现,早期的时候应用比较普遍。但是,该方法会在新图像中产生明显的锯齿边缘和马赛克现象。双线性插值法具有平滑功能,能有效地克服邻法的不足,但会退化图像的高频部分,使图像细节变模糊。在放大倍数比较高时,高阶插值,如双三次和三次样条插值等比低阶插值效果好。这些插值算法可以使插值生成的像素灰度值延续原图像灰度变化的连续性,从而使放大图像浓淡变化自然平滑。但是在图像中,有些像素与相邻像素间灰度值存在突变,即存在灰度不连续性。这些具有灰度值突变的像素就是图像中描述对象的轮廓或纹理图像的边缘像素。在图像放大中,对这些具有不连续灰度特性的像素,如果采用常规的插值算法生成新增加的像素,势必会使放大图像的轮廓和纹理模糊,降低图像质量。

基于边缘的图像插值算法

为了克服传统方法的不足, 提出了许多边缘保护的插值方法,对插值图像的边缘有一定的增强, 使得图像的视觉效果更好, 边缘保护的插值方法可以分为两类: 基于原始低分辨图像边缘的方法和基于插值后高分辨率图像边缘的方法。基于原始低分辨率图像边缘的方法:(1)首先检测低分辨率图像的边缘, 然后根据检测的边缘将像素分类处理, 对于平坦区域的像素,采用传统方法插值;对于边缘区域的像素, 设计特殊插值方法, 以达到保持边缘细节的目的。(2)基于插值后高分辨率图像边缘的方法这类插值方法:首先采用传统方法插值低分辨率图像,然后检测高分辨率图像的边缘,最后对边缘及附近像素进行特殊处理, 以去除模糊, 增强图像的边缘。

基于区域的图像插值算法

首先将原始低分辨率图像分割成不同区域,然后将插值点映射到低分辨率图像, 判断其所属区域, 最后根据插值点的邻域像素设计不同的插值公式, 计算插值点的值。

官方包golang.org/x/image/draw

提供了对图像高级操作。

采用双线性插值算法(bilinear)来放大图像。

import draw2 "golang.org/x/image/draw"
func image13() {
   f, err := os.Open("./image1.jpg")
   if err != nil {
      panic(err)
   }
   defer f.Close()
   src, _, _ := image.Decode(f)

   scale := 2
   dstWidth := src.Bounds().Max.X * scale
   dstHeight := src.Bounds().Max.Y * scale

   dr := image.Rect(0, 0, dstWidth, dstHeight)
   dst := image.NewRGBA(dr)

   draw2.BiLinear.Scale(dst, dr, src, src.Bounds(), draw2.Src, nil)

   f1, _ := os.Create("./image13.jpg")
   defer f1.Close()

   b := bufio.NewWriter(f1)
   jpeg.Encode(b, dst, nil)
   b.Flush()
}

golang.org/x/image/draw 提供了四种插值算法,他们的特点在注释中已经说明了。

var (
   // NearestNeighbor is the nearest neighbor interpolator. It is very fast,
   // but usually gives very low quality results. When scaling up, the result
   // will look 'blocky'.
   NearestNeighbor = Interpolator(nnInterpolator{})

   // ApproxBiLinear is a mixture of the nearest neighbor and bi-linear
   // interpolators. It is fast, but usually gives medium quality results.
   //
   // It implements bi-linear interpolation when upscaling and a bi-linear
   // blend of the 4 nearest neighbor pixels when downscaling. This yields
   // nicer quality than nearest neighbor interpolation when upscaling, but
   // the time taken is independent of the number of source pixels, unlike the
   // bi-linear interpolator. When downscaling a large image, the performance
   // difference can be significant.
   ApproxBiLinear = Interpolator(ablInterpolator{})

   // BiLinear is the tent kernel. It is slow, but usually gives high quality
   // results.
   BiLinear = &Kernel{1, func(t float64) float64 {
      return 1 - t
   }}

   // CatmullRom is the Catmull-Rom kernel. It is very slow, but usually gives
   // very high quality results.
   //
   // It is an instance of the more general cubic BC-spline kernel with parameters
   // B=0 and C=0.5. See Mitchell and Netravali, "Reconstruction Filters in
   // Computer Graphics", Computer Graphics, Vol. 22, No. 4, pp. 221-228.
   CatmullRom = &Kernel{2, func(t float64) float64 {
      if t < 1 {
         return (1.5*t-2.5)*t*t + 1
      }
      return ((-0.5*t+2.5)*t-4)*t + 2
   }}

   // TODO: a Kaiser-Bessel kernel?
)

其他功能,比如将文字写入图像,数字验证码,水印等等都是基于此包。

参考 https://blog.csdn.net/picone/article/details/123788128

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis实现消息队列

7、Redis消息队列 7.1 Redis消息队列-认识消息队列 什么是消息队列&#xff1a;字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色&#xff1a; 消息队列&#xff1a;存储和管理消息&#xff0c;也被称为消息代理&#xff08;Message Broker&#xff09;生产者&…

定位tcp连接或端口是属于哪个进程

首先要知道tcp连接的端口号&#xff0c;要么是本地端口号要是么remote端口号。有的端口号我们已知&#xff0c;有的端口号可以通过日志获取&#xff0c;也可以抓包获取&#xff0c;如然后是用netstat命令获取pidlinux下使用netstat -anpt&#xff08;a是输出所有n是展示端口号&…

【✓基础算法 2.4】KMP(完结)

当模式串和主串的子串有不匹配时&#xff0c;便往后退一步&#xff0c;看是否能走通&#xff0c;如果不能&#xff0c;则进行退—— KMP 目录 一、KMP算法简介 二、手算求next数组 三、next数组实现代码 四、完整代码实现 1、java 2、c 一、KMP算法简介 当主串的子串…

软件测试刚入职,很茫然怎么办~

毕业后能直接到公司开始工作&#xff0c;是多少人都想要却得不到的机遇&#xff0c;你现在茫然无非是因为对软件测试是做什么的不了解&#xff0c;也不知道软件测试的职业规划到底是怎么样的~ 既然已经进入了公司&#xff0c;那就要珍惜机会&#xff0c;多学习&#xff0c;多问…

redis7 Cluster模式 集群

1.Redis集群模式介绍 Cluster模式是Redis3.0开始推出的Redis Cluster属于AP模型采用无中心结构&#xff0c;每个节点保存数据和整个集群状态, 每个节点都和其他所有节点连接官方要求&#xff1a;至少6个节点才可以保证高可用&#xff0c;即3主3从&#xff1b;扩展性强、更好做到…

[GUET-CTF2019]re

于尘世中携一支笔&#xff0c;绘春风十里。 1.查壳 64bit加了UPX壳 2.使用Kali Linux脱壳 re脱壳3.静态分析 shift加F12打开string窗口 发现可疑字符&#xff0c;点击跟进 光标放在aCorrect&#xff0c;点击快捷键X查看引用这个字符串的地方 点击跟进 按下F5反编译 将函数重命…

为什么AI爆炸式增长会对元宇宙产生巨大影响

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;9分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 想想你梦想中的房子。也许它有高高的拱形天花板、温暖的壁炉和宽阔的窗户&…

Allegro如何打开丝印位号的飞线操作指导

Allegro如何打开丝印位号的飞线操作指导 用Allegro做PCB设计的时候,移动器件的位号,会有根飞线实时提示位号是属于哪个器件的,如下图 但是只有在临时移动丝印位号的时候才会显示飞线,如何让所有的器件位号的全部显示,具体操作如下 选择Setup选择User Preferences

如何通过Zabbix Docker配置HTTPS访问系统?

概述 前面文章曾介绍过如果使用docker-compose快速部署一个Zabbix系统&#xff0c;但是部署的Zabbix系统是使用http协议进行访问的。有时候为了保证安全。我们需要配置使用https协议进行访问。 下面就讲述如何使用自签名的ssl证书配置https访问。&#xff08;注&#xff1a;若是…

开发者对抗软件创新焦虑的“180 法则” | 对话MongoDB CTO Mark Porter

在 MongoDB 首席技术官 Mark Porter 看来&#xff0c;创新滞后并不是因为公司缺乏灵感或创造力&#xff0c;而是因为他们被迫将时间花费在维护传统框架上&#xff0c;导致数据相关工作举步维艰&#xff0c;这是大多数组织都存在的问题。那么&#xff0c;对于企业而言&#xff0…

千姿百态,瞬息万变,Win11系统NeoVim打造全能/全栈编辑器(前端/Css/Js/Vue/Golang/Ruby/ChatGpt)

我曾经多次向人推荐Vim&#xff0c;其热情程度有些类似现在卖保险的&#xff0c;有的时候&#xff0c;人们会因为一些弥足珍贵的美好暗暗渴望一个巨大的负面&#xff0c;比如因为想重温手动挡的快乐而渴望买下一辆二十万公里的老爷车&#xff0c;比如因为所谓完美的音质而舍不得…

xxl-sso知识点

1、哈希环算法&#xff1a; 通过将数据分配到一个环形的哈希表上来实现数据的分布&#xff0c;根据环上的顺序依次进行分配。 对于数据敏感的服务不能用取余hash来实现&#xff0c;当新增节点时之前按照取余hash放的数据在新增节点之后在相应的节点找不到数据&#xff0c;而哈…

“华为杯”研究生数学建模竞赛2005年-【华为杯】B题:空中加油

赛题描述 对飞行中的飞机进行空中加油,可以大大提高飞机的直航能力。为了简化问题,便于讨论,我们作如下假设。 少辅机架数两种情况给出你的作战方案。 解题思路 摘要: 本文讨论了在辅机只能一次起飞和可以多次起飞情况下,辅机数量与主机 最大作战半径的关系,给出了只…

【前端工程化】一.前端工程化;二.使用webpack打包vue项目;三.在webstorm中安装vite插件,通过vite构建工具创建vue项目

目录 一.前端工程化 1.webpack &#xff08;1&#xff09;定义&#xff1a;是一个前端的构建工具。前端代码格式多&#xff08;html、css、js、图片&#xff09;&#xff0c;前端构建工具的作用是将各种格式不同文件打包到一起&#xff0c;方便项目的上线运行。&#xff08;将…

60 k8s集群中container监控指标有label但是没有value

k8s集群中container监控指标有label但是没有value 1、现象 无法获取到以下label的值:container, image, pod, name 集群刚搭建完的时候一切都是正常的,后来运行一段时间后就缺这缺那的。 2、原因排查 由于这些指标是kubelet上报的,因此看了对应节点上kubelet的日志,发现…

从事Java开发学历是门槛吗?

“学历低&#xff0c;工作好找吗&#xff0c;以后跳槽、转型会不会有门槛&#xff1f;”是的&#xff0c;学历这块敲门砖&#xff0c;在哪里都适用。工厂流水线里学历高的更容易被提拔为管理者&#xff0c;学历越高&#xff0c;对于工具的使用越好。门槛越高&#xff0c;薪资待…

代码随想录算法训练营第五十六天_第九章_动态规划 | 300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

LeetCode 300.最长递增子序列 给你一个整数数组 nums &#xff0c;返回其中最长递增子序列的长度。 视频讲解https://www.bilibili.com/video/BV1ng411J7xP文章讲解 https://programmercarl.com/0300.%E6%9C%80%E9%95%BF%E4%B8%8A%E5%8D%87%E5%AD%90%E5%BA%8F%E5%88%97.html 思…

蓝库云|一文搞懂什么是MES(制造执行系统),制造业该如何选择

MES&#xff08;Manufacturing Execution System&#xff09;是一种将制造过程可视化并为工人提供指导和支持的系统。它的优势在于可以优化人员、机器和设备等资源&#xff0c;消除制造对个人技能的依赖。在本文中&#xff0c;蓝库云将以通俗易懂的方式说明MES的概要以及与生产…

CS5260电路参考|Type-C转VGA转换器方案|Type-C转VGA转接线方案

CS5260电路参考|Type-C转VGA转换器方案|Type-C转VGA转接线方案 CS5260是一款TYPEC转VGA转换方案芯片。 CS5260作为标准的USB Type-C显示端口Alt模式接收器&#xff0c;CS5260由双通道主链路差分对&#xff0c;一个AUX通道差分对组成。 CS5260主链接作为双通道差分对&#x…

npm Eslint 报错

❤ npm Eslint 报错 npm init --yes初始化项目 ○ 安装ESLint模块 npm add eslint --dev&#xff0c;node-modules/.bin目录生成eslint的cli程序 ○ 查看eslint的版本 使用npm eslint --version ○ 使用yarn eslint ./index.js 当代码中出现语法错误时&#xff0c;eslint无法检…