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,8,8 //status same划線狀態,gap允許色差 z多少個差別 h單個色塊高 w單個色塊寬
var outlines []outline = make([]outline,0,(width+high)/64)

b := img1.Bounds()
//根據b畫布的大小新建一個新圖像
m := image.NewRGBA(b)
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 {
for j:=0 ; j < high ; j+=h {
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 {
drawline(i,j,4,2,w,m)
status = 1
}
z++
}
if status == 1 && distance <= gap {
outlines = append(outlines, outline{
x:i,
y:j,
})
drawline(i,j,4,3,w,m)
status,same = 0, 1
}
} //w
}//h

name1 := strconv.Itoa(int(time.Now().Unix()))
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))
sortline(outlines)
for k,v := range outlines{
if k == 0 {
status, same= 0,0
}
if k+1 == len(outlines) {
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 != outlines[k-1].y ) && status == 1{
drawline(outlines[k-1].x,outlines[k-1].y,4,1,w,m)
same,status = 0, 0
}
}

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("切片大小 不同圖像塊 每次對比寬度分別是:", 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) {
oy,startkey := -1,0
if len(outlines) > 0 {
oy = outlines[0].y
}
var sortx Outlinesort
for key,value := range outlines {
if value.y != oy {
sortx = outlines[startkey:key]
sort.Sort(Outlinesortx(sortx))
startkey,oy = key,value.y
}
if key == outlines.Len() {
sortx = outlines[startkey:key]
sort.Sort(Outlinesortx(sortx))
}
}
}

 

 

 

 

方式二

有點簡單粗暴,直接逐個對比像素點。前提是要兩張大小 輪廓 位置相同的圖片。本來是設想對比相片是否相同,是否存在差異,固定架拍攝,因此輪廓相同。  后期考慮做圖片位置重合匹配

https://gitee.com/shuidaan/golang/raw/master/ImageDifferencePx.go 源碼

package main

import (
"errors"
"fmt"
"image"
"image/color"
"image/draw"
"image/jpeg"
"math"
"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() {
t2 := time.Now().Nanosecond()
file1, _ := os.Open(".\\img\\a0.jpg")
file2, _ := os.Open(".\\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 {
z++
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
}
} //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))
sortline(outlines)
for k,v := range outlines{
if k == 0 {
status, same= 0,0
}
if k+1 == len(outlines) {
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 != outlines[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 outlines{
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) {
oy,startkey := -1,0
if len(outlines) > 0 {
oy = outlines[0].y
}
var sortx Outlinesort
for key,value := range outlines {
if value.y != oy {
sortx = outlines[startkey:key]
sort.Sort(Outlinesortx(sortx))
startkey,oy = key,value.y
}
if key == outlines.Len() {
sortx = outlines[startkey:key]
sort.Sort(Outlinesortx(sortx))
}
}
}
//將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
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM