go 图片相似 查找两张图片不同的部分 找出两幅图片中的不同处
作者:互联网
golang Image similarity comparison go语言相似图像对比不同
目前网上找了很多的 图片相似 查找两张图片不同的部分,实现图像比较和标记以显示不同 ,很多都是python写的,没有找到go语言写的,所以想自己写一个
图片A 为参照物,去寻找图片B 的最佳重合位置,然后再查找图片的不同之处。这一步暂时未做,以后如果有时间或者相关需要 再写。 目前图片轮廓相同 未裁剪。 目前设想,可以截取图片A的部分 取匹配到图片B的位置上,然后再对比两个范围内的像素。
代码未优化 ,需改进。
AverageHash DifferenceHash PerceptionHash 三种常用算法,适合比对相似图片,我们是求差别。这三种算法,作为图片的相似性识别挺好。但是用来找差异,有些处理结果让人费解。
这是方式一 go/ImageDifferenceHash.go at main · shuidaan/go · GitHub
package main import ( "errors" "fmt" "github.com/corona10/goimagehash" "image" "image/color" "image/draw" "image/jpeg" "os" "sort" "strconv" "time" ) type outline struct { x int y int } type Outlinesort []outline type Outlinesortx []outline func (o Outlinesort) Len() int { //返回传入数据的总数 return len(o) } func (o Outlinesort) Swap(i, j int) { //两个对象满足Less()则位置对换 //表示执行交换数组中下标为i的数据和下标为j的数据 o[i], o[j] = o[j], o[i] } func (o Outlinesort) Less(i, j int) bool { //按字段比较大小,此处是降序排序 //返回数组中下标为i的数据是否小于下标为j的数据 return o[i].y < o[j].y } func (o Outlinesortx) Len() int { //返回传入数据的总数 return len(o) } func (o Outlinesortx) Swap(i, j int) { //两个对象满足Less()则位置对换 //表示执行交换数组中下标为i的数据和下标为j的数据 o[i], o[j] = o[j], o[i] } func (o Outlinesortx) Less(i, j int) bool { //按字段比较大小,此处是降序排序 //返回数组中下标为i的数据是否小于下标为j的数据 return o[i].x < o[j].x } func main() { file1, _ := os.Open("E:\\wq\\img\\a0.jpg") file2, _ := os.Open("E:\\wq\\img\\a8.jpg") defer file1.Close() defer file2.Close() img1, _ := jpeg.Decode(file1) img2, _ := jpeg.Decode(file2) width, high := img1.Bounds().Dx(),img1.Bounds().Dy() var status,same, gap, z,h,w int = 0,1,1,0,1,1 //status same划线状态,gap允许色差 z多少个差别 h单个色块高 w单个色块宽 var outlines []outline = make([]outline,0,(width+high)/64) b := img1.Bounds() //根据b画布的大小新建一个新图像 m := image.NewRGBA(b) fmt.Println(width,high) draw.Draw(m, b, img1, b.Min, draw.Over) ////测试被裁剪的小图是否全部加入对比 //sb1 := img1.Bounds() ////根据b画布的大小新建一个新图像 //sm1 := image.NewRGBA(sb1) // //sb2 := img1.Bounds() ////根据b画布的大小新建一个新图像 //sm2 := image.NewRGBA(sb2) for i:= 0;i < width ; i+=w { //if (width - i < 16) i = width- i //x = i //if (width - i < 16) { // x = width- i //} for j:=0 ; j < high ; j+=h { //y=j //if (high - j < 16) { // y = high- j // //} subimg1,err := clip(img1,i,j,w,h) if err != nil { fmt.Println(err) } subimg2,err := clip(img2,i,j,w,h) if err != nil { fmt.Println(err) } //soffet1 := image.Pt(i,j) //ssr1 := subimg2.Bounds() //draw.Draw(sm1,sb1,subimg1,ssr1.Min.Sub(soffet1),draw.Over) //soffet2 := image.Pt(i,j) //ssr2 := subimg2.Bounds() //draw.Draw(sm2,sb2,subimg2,ssr2.Min.Sub(soffet2),draw.Over) hash1, _ := goimagehash.DifferenceHash(subimg1) //AverageHash DifferenceHash PerceptionHash 三种常用算法 hash2, _ := goimagehash.DifferenceHash(subimg2) distance, err := hash1.Distance(hash2) if err != nil { fmt.Println(err) } if distance > gap { offet := image.Pt(i,j) sr := subimg2.Bounds() outlines = append(outlines, outline{ x:i, y:j, }) draw.Draw(m,b,subimg2,sr.Min.Sub(offet),draw.Over) if status == 0 && same == 1 { outlines = append(outlines, outline{ x:i, y:j, }) drawline(i,j,4,2,w,m) status = 1 } offet,sr = image.ZP ,image.ZR } if status == 1 && distance <= gap { outlines = append(outlines, outline{ x:i, y:j, }) drawline(i,j,4,3,w,m) status,same = 0, 1 } z++ } //w }//h name1 := strconv.Itoa(int(time.Now().Unix())) fmt.Println(name1) imgw, err := os.Create(name1 + "shuidaan.jpg") if err != nil { fmt.Println(err) } //测试被裁剪的小图是否全部加入对比 //simgw1, err := os.Create(name1 + "new1.jpg") //if err != nil { // fmt.Println(err) //} //simgw2, err := os.Create(name1 + "new2.jpg") //if err != nil { // fmt.Println(err) //} //sort.Sort(Outlinesortx(outlines)) sort.Sort(Outlinesort(outlines)) sortlx := sortline(outlines) for k,v := range sortlx{ if k == 0 { status, same= 0,0 } if k+1 == sortlx.Len() { drawline(outlines[k].x,outlines[k].y,4,1,w,m) } if status == 0 && same == 0 { drawline(v.x,v.y,4,0,w,m) same, status = v.x,1 //fmt.Println(same,v.y,status) //fmt.Println(same,v.x,v.y,oy,"___") continue } if v.x - same == w { same, status= v.x,1 } //fmt.Println(same,v.x,sortlx[k].x,v.y, sortlx[k].y ,"__1_") if (v.x - same > w || v.y != sortlx[k-1].y ) && status == 1{ //fmt.Println(k) //fmt.Println(v.x,v.y) // 取K值,无法应用 drawline(outlines[k-1].x,outlines[k-1].y,4,1,w,m) same,status = 0, 0 //fmt.Println(same,v.x,v.y,oy) } } //fmt.Println(outlines) jpeg.Encode(imgw, m, &jpeg.Options{100}) defer imgw.Close() // // 测试被裁剪的小图是否全部加入对比 //jpeg.Encode(simgw1, sm1, &jpeg.Options{100}) //defer simgw1.Close() // //jpeg.Encode(simgw2, sm2, &jpeg.Options{100}) //defer simgw2.Close() fmt.Println(time.Now().Unix() , cap(outlines),z,w) } func clip(src image.Image, x, y, w, h int) (image.Image, error) { var subImg image.Image if rgbImg, ok := src.(*image.YCbCr); ok { subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.YCbCr) //图片裁剪x0 y0 x1 y1 } else if rgbImg, ok := src.(*image.RGBA); ok { subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.RGBA) //图片裁剪x0 y0 x1 y1 } else if rgbImg, ok := src.(*image.NRGBA); ok { subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.NRGBA) //图片裁剪x0 y0 x1 y1 } else { return subImg, errors.New("图片解码失败") } return subImg, nil } func drawline(x, y, size, dire, zone int, m *image.RGBA) error { //x,y划线起点坐标 size线粗 dire线方向 zone对比像素大小 size+=4 switch dire { case 0: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x-dot,y+z,color.RGBA{255, 0, 0, 255}) } } case 1: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x+dot+zone,y+z,color.RGBA{0, 255, 0, 255}) } } case 2: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x+z,y-dot,color.RGBA{255, 0, 0, 255}) } } case 3: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x+z,y+dot,color.RGBA{0, 255, 0, 255}) } } default: return errors.New("Parameter error") } return nil } func sortline(outlines Outlinesort) (Outlinesort) { oy,startkey := -1,0 var sortx,sortxx Outlinesort for key,value := range outlines { if value.y != oy { sortx = outlines[startkey:key] sort.Sort(Outlinesortx(sortx)) sortxx = append(sortxx,sortx...) startkey,oy = key,value.y } if key == outlines.Len() { sortx = outlines[startkey:key] sort.Sort(Outlinesortx(sortx)) sortxx = append(sortxx,sortx...) } } return sortxx }
方式二
有点简单粗暴,直接逐个对比像素点。前提是要两张大小 轮廓 位置相同的图片。本来是设想对比相片是否相同,是否存在差异,固定架拍摄,因此轮廓相同。 后期考虑做图片位置重合匹配
https://gitee.com/shuidaan/golang/raw/master/ImageDifferencePx.go 源码
type outline struct { x int y int } type Outlinesort []outline type Outlinesortx []outline func (o Outlinesort) Len() int { //返回传入数据的总数 return len(o) } func (o Outlinesort) Swap(i, j int) { //两个对象满足Less()则位置对换 //表示执行交换数组中下标为i的数据和下标为j的数据 o[i], o[j] = o[j], o[i] } func (o Outlinesort) Less(i, j int) bool { //按字段比较大小,此处是降序排序 //返回数组中下标为i的数据是否小于下标为j的数据 return o[i].y < o[j].y } func (o Outlinesortx) Len() int { //返回传入数据的总数 return len(o) } func (o Outlinesortx) Swap(i, j int) { //两个对象满足Less()则位置对换 //表示执行交换数组中下标为i的数据和下标为j的数据 o[i], o[j] = o[j], o[i] } func (o Outlinesortx) Less(i, j int) bool { //按字段比较大小,此处是降序排序 //返回数组中下标为i的数据是否小于下标为j的数据 return o[i].x < o[j].x } func main() { t2 := time.Now().Nanosecond() file1, _ := os.Open("E:\\wq\\img\\a0.jpg") file2, _ := os.Open("E:\\wq\\img\\a8.jpg") defer file1.Close() defer file2.Close() img1, _ := jpeg.Decode(file1) img2, _ := jpeg.Decode(file2) width, high := img1.Bounds().Dx(),img1.Bounds().Dy() var status,same, z,h,w int = 0,1,0,1,1 //status same划线状态,gap允许色差 z多少个差别 h单个色块高 w单个色块宽 var gap float64 = 1 var outlines []outline = make([]outline,0,(width*high)/32) b := img1.Bounds() //根据b画布的大小新建一个新图像 m := image.NewRGBA(b) draw.Draw(m, b, img1, b.Min, draw.Over) for i:= 0;i < width ; i+=w { for j:=0 ; j < high ; j+=h { subimg1px := rgb2gray1px(img1.At(i,j)) subimg2px := rgb2gray1px(img2.At(i,j)) //AverageHash DifferenceHash PerceptionHash 三种常用算法,适合比对相似图片。我们是求差别 distance := math.Abs(subimg1px - subimg2px) if distance > gap { outlines = append(outlines, outline{ x:i, y:j, }) if status == 0 && same == 1 { outlines = append(outlines, outline{ x:i, y:j, }) drawline(i,j,4,2,w,m) //竖向画框 status = 1 } } if status == 1 && distance <= gap { outlines = append(outlines, outline{ x:i, y:j, }) drawline(i,j,4,3,w,m) //竖向画框 status,same = 0, 1 } z++ } //w }//h name1 := strconv.Itoa(int(time.Now().Unix())) imgw, err := os.Create(name1 + "shuidaan.jpg") if err != nil { fmt.Println(err) } sort.Sort(Outlinesort(outlines)) sortlx := sortline(outlines) //横向画框 for k,v := range sortlx{ if k == 0 { status, same= 0,0 } if k+1 == sortlx.Len() { drawline(outlines[k].x,outlines[k].y,4,1,w,m) } if status == 0 && same == 0 { drawline(v.x,v.y,4,0,w,m) same, status = v.x,1 continue } if v.x - same == w { same, status= v.x,1 } if (v.x - same > w || v.y != sortlx[k-1].y ) && status == 1{ drawline(outlines[k-1].x,outlines[k-1].y,4,1,w,m) same,status = 0, 0 } } for _,v := range sortlx{ m.Set(v.x,v.y,img2.At(v.x,v.y)) } jpeg.Encode(imgw, m, &jpeg.Options{100}) defer imgw.Close() t3:= time.Now().Nanosecond() -t2 fmt.Printf("This picture width is %d,height is %d pixels. The program runs for %d milliseconds. There are %d pixels that are different \n",width,high,t3/1e6,len(outlines)) fmt.Printf("图片宽 %d,高 %d 像素. 程序运行耗时 %d 毫秒. 相片有 %d 像素不同 \n",width,high,t3/1e6,len(outlines)) } //由点划线 func drawline(x, y, size, dire, zone int, m *image.RGBA) error { //x,y划线起点坐标 size线粗 dire线方向 zone对比像素大小 size+=4 switch dire { case 0: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x-dot,y+z,color.RGBA{255, 0, 0, 255}) } } case 1: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x+dot+zone,y+z,color.RGBA{0, 255, 0, 255}) } } case 2: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x+z,y-dot,color.RGBA{255, 0, 0, 255}) } } case 3: for dot := 4;dot < size;dot++ { for z:= 0;z < zone ;z++ { m.Set(x+z,y+dot,color.RGBA{0, 255, 0, 255}) } } default: return errors.New("Parameter error") } return nil } // 排序,用于框出差异,优化减少重复设置像素 func sortline(outlines Outlinesort) (Outlinesort) { oy,startkey := -1,0 var sortx,sortxx Outlinesort for key,value := range outlines { //if oy == -1 { // oy = value.y //} if value.y != oy { sortx = outlines[startkey:key] sort.Sort(Outlinesortx(sortx)) sortxx = append(sortxx,sortx...) startkey,oy = key,value.y } if key == outlines.Len() { sortx = outlines[startkey:key] sort.Sort(Outlinesortx(sortx)) sortxx = append(sortxx,sortx...) } } return sortxx } //将rgb像素转化为gray,用于对比色差 func rgb2gray1px(colorImg color.Color) float64 { r, g, b, _ := colorImg.RGBA() lum := 0.299*float64(r/257) + 0.587*float64(g/257) + 0.114*float64(b/256) return lum }
标签:return,int,image,两幅,func,go,outlines,dot,图片 来源: https://www.cnblogs.com/shuidaan/p/15316396.html