当前位置:   article > 正文

go 图片相似 查找两张图片不同的部分 找出两幅图片中的不同处_goimagehash

goimagehash

golang Image similarity comparison

目前网上找了很多的 图片相似 查找两张图片不同的部分,实现图像比较和标记以显示不同 ,很多都是python写的,没有找到go语言写的,所以想自己写一个

图片A 为参照物,去寻找图片B 的最佳重合位置,然后再查找图片的不同之处。这一步暂时未做,以后如果有时间或者相关需要 再写。 目前图片轮廓相同 未裁剪。 目前设想,可以截取图片A的部分 取匹配到图片B的位置上,然后再对比两个范围内的像素。

 代码未优化 ,需改进。

AverageHash DifferenceHash PerceptionHash 三种常用算法,适合比对相似图片,我们是求差别。这三种算法,作为图片的相似性识别挺好。但是用来找差异,有些处理结果让人费解。

这是方式一  go/ImageDifferenceHash.go at main · shuidaan/go · GitHub

  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/corona10/goimagehash"
  6. "image"
  7. "image/color"
  8. "image/draw"
  9. "image/jpeg"
  10. "os"
  11. "sort"
  12. "strconv"
  13. "time"
  14. )
  15. type outline struct {
  16. x int
  17. y int
  18. }
  19. type Outlinesort []outline
  20. type Outlinesortx []outline
  21. func (o Outlinesort) Len() int {
  22. //返回传入数据的总数
  23. return len(o)
  24. }
  25. func (o Outlinesort) Swap(i, j int) {
  26. //两个对象满足Less()则位置对换
  27. //表示执行交换数组中下标为i的数据和下标为j的数据
  28. o[i], o[j] = o[j], o[i]
  29. }
  30. func (o Outlinesort) Less(i, j int) bool {
  31. //按字段比较大小,此处是降序排序
  32. //返回数组中下标为i的数据是否小于下标为j的数据
  33. return o[i].y < o[j].y
  34. }
  35. func (o Outlinesortx) Len() int {
  36. //返回传入数据的总数
  37. return len(o)
  38. }
  39. func (o Outlinesortx) Swap(i, j int) {
  40. //两个对象满足Less()则位置对换
  41. //表示执行交换数组中下标为i的数据和下标为j的数据
  42. o[i], o[j] = o[j], o[i]
  43. }
  44. func (o Outlinesortx) Less(i, j int) bool {
  45. //按字段比较大小,此处是降序排序
  46. //返回数组中下标为i的数据是否小于下标为j的数据
  47. return o[i].x < o[j].x
  48. }
  49. func main() {
  50. file1, _ := os.Open("E:\\wq\\img\\a0.jpg")
  51. file2, _ := os.Open("E:\\wq\\img\\a8.jpg")
  52. defer file1.Close()
  53. defer file2.Close()
  54. img1, _ := jpeg.Decode(file1)
  55. img2, _ := jpeg.Decode(file2)
  56. width, high := img1.Bounds().Dx(),img1.Bounds().Dy()
  57. var status,same, gap, z,h,w int = 0,1,1,0,8,8 //status same划线状态,gap允许色差 z多少个差别 h单个色块高 w单个色块宽
  58. var outlines []outline = make([]outline,0,(width+high)/64)
  59. b := img1.Bounds()
  60. //根据b画布的大小新建一个新图像
  61. m := image.NewRGBA(b)
  62. draw.Draw(m, b, img1, b.Min, draw.Over)
  63. 测试被裁剪的小图是否全部加入对比
  64. //sb1 := img1.Bounds()
  65. 根据b画布的大小新建一个新图像
  66. //sm1 := image.NewRGBA(sb1)
  67. //
  68. //sb2 := img1.Bounds()
  69. 根据b画布的大小新建一个新图像
  70. //sm2 := image.NewRGBA(sb2)
  71. for i:= 0;i < width ; i+=w {
  72. for j:=0 ; j < high ; j+=h {
  73. subimg1,err := clip(img1,i,j,w,h)
  74. if err != nil {
  75. fmt.Println(err)
  76. }
  77. subimg2,err := clip(img2,i,j,w,h)
  78. if err != nil {
  79. fmt.Println(err)
  80. }
  81. //soffet1 := image.Pt(i,j)
  82. //ssr1 := subimg2.Bounds()
  83. //draw.Draw(sm1,sb1,subimg1,ssr1.Min.Sub(soffet1),draw.Over)
  84. //soffet2 := image.Pt(i,j)
  85. //ssr2 := subimg2.Bounds()
  86. //draw.Draw(sm2,sb2,subimg2,ssr2.Min.Sub(soffet2),draw.Over)
  87. hash1, _ := goimagehash.DifferenceHash(subimg1) //AverageHash DifferenceHash PerceptionHash 三种常用算法
  88. hash2, _ := goimagehash.DifferenceHash(subimg2)
  89. distance, err := hash1.Distance(hash2)
  90. if err != nil {
  91. fmt.Println(err)
  92. }
  93. if distance > gap {
  94. offet := image.Pt(i,j)
  95. sr := subimg2.Bounds()
  96. outlines = append(outlines, outline{
  97. x:i,
  98. y:j,
  99. })
  100. draw.Draw(m,b,subimg2,sr.Min.Sub(offet),draw.Over)
  101. if status == 0 && same == 1 {
  102. drawline(i,j,4,2,w,m)
  103. status = 1
  104. }
  105. z++
  106. }
  107. if status == 1 && distance <= gap {
  108. outlines = append(outlines, outline{
  109. x:i,
  110. y:j,
  111. })
  112. drawline(i,j,4,3,w,m)
  113. status,same = 0, 1
  114. }
  115. } //w
  116. }//h
  117. name1 := strconv.Itoa(int(time.Now().Unix()))
  118. imgw, err := os.Create(name1 + "shuidaan.jpg")
  119. if err != nil {
  120. fmt.Println(err)
  121. }
  122. //测试被裁剪的小图是否全部加入对比
  123. //simgw1, err := os.Create(name1 + "new1.jpg")
  124. //if err != nil {
  125. // fmt.Println(err)
  126. //}
  127. //simgw2, err := os.Create(name1 + "new2.jpg")
  128. //if err != nil {
  129. // fmt.Println(err)
  130. //}
  131. //sort.Sort(Outlinesortx(outlines))
  132. sort.Sort(Outlinesort(outlines))
  133. sortline(outlines)
  134. for k,v := range outlines{
  135. if k == 0 {
  136. status, same= 0,0
  137. }
  138. if k+1 == len(outlines) {
  139. drawline(outlines[k].x,outlines[k].y,4,1,w,m)
  140. }
  141. if status == 0 && same == 0 {
  142. drawline(v.x,v.y,4,0,w,m)
  143. same, status = v.x,1
  144. continue
  145. }
  146. if v.x - same == w {
  147. same, status= v.x,1
  148. }
  149. if (v.x - same > w || v.y != outlines[k-1].y ) && status == 1{
  150. drawline(outlines[k-1].x,outlines[k-1].y,4,1,w,m)
  151. same,status = 0, 0
  152. }
  153. }
  154. jpeg.Encode(imgw, m, &jpeg.Options{100})
  155. defer imgw.Close()
  156. // // 测试被裁剪的小图是否全部加入对比
  157. //jpeg.Encode(simgw1, sm1, &jpeg.Options{100})
  158. //defer simgw1.Close()
  159. //
  160. //jpeg.Encode(simgw2, sm2, &jpeg.Options{100})
  161. //defer simgw2.Close()
  162. fmt.Println("切片大小 不同图像块 每次对比宽度分别是:", cap(outlines),z,w)
  163. }
  164. func clip(src image.Image, x, y, w, h int) (image.Image, error) {
  165. var subImg image.Image
  166. if rgbImg, ok := src.(*image.YCbCr); ok {
  167. subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.YCbCr) //图片裁剪x0 y0 x1 y1
  168. } else if rgbImg, ok := src.(*image.RGBA); ok {
  169. subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.RGBA) //图片裁剪x0 y0 x1 y1
  170. } else if rgbImg, ok := src.(*image.NRGBA); ok {
  171. subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.NRGBA) //图片裁剪x0 y0 x1 y1
  172. } else {
  173. return subImg, errors.New("图片解码失败")
  174. }
  175. return subImg, nil
  176. }
  177. func drawline(x, y, size, dire, zone int, m *image.RGBA) error {
  178. //x,y划线起点坐标 size线粗 dire线方向 zone对比像素大小
  179. size+=4
  180. switch dire {
  181. case 0:
  182. for dot := 4;dot < size;dot++ {
  183. for z:= 0;z < zone ;z++ {
  184. m.Set(x-dot,y+z,color.RGBA{255, 0, 0, 255})
  185. }
  186. }
  187. case 1:
  188. for dot := 4;dot < size;dot++ {
  189. for z:= 0;z < zone ;z++ {
  190. m.Set(x+dot+zone,y+z,color.RGBA{0, 255, 0, 255})
  191. }
  192. }
  193. case 2:
  194. for dot := 4;dot < size;dot++ {
  195. for z:= 0;z < zone ;z++ {
  196. m.Set(x+z,y-dot,color.RGBA{255, 0, 0, 255})
  197. }
  198. }
  199. case 3:
  200. for dot := 4;dot < size;dot++ {
  201. for z:= 0;z < zone ;z++ {
  202. m.Set(x+z,y+dot,color.RGBA{0, 255, 0, 255})
  203. }
  204. }
  205. default:
  206. return errors.New("Parameter error")
  207. }
  208. return nil
  209. }
  210. // 排序,用于框出差异,优化减少重复设置像素 切片指针传递
  211. func sortline(outlines Outlinesort) {
  212. oy,startkey := -1,0
  213. if len(outlines) > 0 {
  214. oy = outlines[0].y
  215. }
  216. var sortx Outlinesort
  217. for key,value := range outlines {
  218. if value.y != oy {
  219. sortx = outlines[startkey:key]
  220. sort.Sort(Outlinesortx(sortx))
  221. startkey,oy = key,value.y
  222. }
  223. if key == outlines.Len() {
  224. sortx = outlines[startkey:key]
  225. sort.Sort(Outlinesortx(sortx))
  226. }
  227. }
  228. }

方式二

有点简单粗暴,直接逐个对比像素点。前提是要两张大小 轮廓 位置相同的图片。本来是设想对比相片是否相同,是否存在差异,固定架拍摄,因此轮廓相同。  后期考虑做图片位置重合匹配

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

  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "image"
  6. "image/color"
  7. "image/draw"
  8. "image/jpeg"
  9. "math"
  10. "os"
  11. "sort"
  12. "strconv"
  13. "time"
  14. )
  15. type outline struct {
  16. x int
  17. y int
  18. }
  19. type Outlinesort []outline
  20. type Outlinesortx []outline
  21. func (o Outlinesort) Len() int {
  22. //返回传入数据的总数
  23. return len(o)
  24. }
  25. func (o Outlinesort) Swap(i, j int) {
  26. //两个对象满足Less()则位置对换
  27. //表示执行交换数组中下标为i的数据和下标为j的数据
  28. o[i], o[j] = o[j], o[i]
  29. }
  30. func (o Outlinesort) Less(i, j int) bool {
  31. //按字段比较大小,此处是降序排序
  32. //返回数组中下标为i的数据是否小于下标为j的数据
  33. return o[i].y < o[j].y
  34. }
  35. func (o Outlinesortx) Len() int {
  36. //返回传入数据的总数
  37. return len(o)
  38. }
  39. func (o Outlinesortx) Swap(i, j int) {
  40. //两个对象满足Less()则位置对换
  41. //表示执行交换数组中下标为i的数据和下标为j的数据
  42. o[i], o[j] = o[j], o[i]
  43. }
  44. func (o Outlinesortx) Less(i, j int) bool {
  45. //按字段比较大小,此处是降序排序
  46. //返回数组中下标为i的数据是否小于下标为j的数据
  47. return o[i].x < o[j].x
  48. }
  49. func main() {
  50. t2 := time.Now().Nanosecond()
  51. file1, _ := os.Open(".\\img\\a0.jpg")
  52. file2, _ := os.Open(".\\img\\a8.jpg")
  53. defer file1.Close()
  54. defer file2.Close()
  55. img1, _ := jpeg.Decode(file1)
  56. img2, _ := jpeg.Decode(file2)
  57. width, high := img1.Bounds().Dx(),img1.Bounds().Dy()
  58. var status,same, z,h,w int = 0,1,0,1,1 //status same划线状态,gap允许色差 z多少个差别 h单个色块高 w单个色块宽
  59. var gap float64 = 1
  60. var outlines []outline = make([]outline,0,(width*high)/32)
  61. b := img1.Bounds()
  62. //根据b画布的大小新建一个新图像
  63. m := image.NewRGBA(b)
  64. draw.Draw(m, b, img1, b.Min, draw.Over)
  65. for i:= 0;i < width ; i+=w {
  66. for j:=0 ; j < high ; j+=h {
  67. subimg1px := rgb2gray1px(img1.At(i,j))
  68. subimg2px := rgb2gray1px(img2.At(i,j))
  69. //AverageHash DifferenceHash PerceptionHash 三种常用算法,适合比对相似图片。我们是求差别
  70. distance := math.Abs(subimg1px - subimg2px)
  71. if distance > gap {
  72. z++
  73. outlines = append(outlines, outline{
  74. x:i,
  75. y:j,
  76. })
  77. if status == 0 && same == 1 {
  78. //outlines = append(outlines, outline{
  79. // x:i,
  80. // y:j,
  81. //})
  82. drawline(i,j,4,2,w,m) //竖向画框
  83. status = 1
  84. }
  85. }
  86. if status == 1 && distance <= gap {
  87. outlines = append(outlines, outline{
  88. x:i,
  89. y:j,
  90. })
  91. drawline(i,j,4,3,w,m) //竖向画框
  92. status,same = 0, 1
  93. }
  94. } //w
  95. }//h
  96. name1 := strconv.Itoa(int(time.Now().Unix()))
  97. imgw, err := os.Create(name1 + "shuidaan.jpg")
  98. if err != nil {
  99. fmt.Println(err)
  100. }
  101. sort.Sort(Outlinesort(outlines))
  102. sortline(outlines)
  103. for k,v := range outlines{
  104. if k == 0 {
  105. status, same= 0,0
  106. }
  107. if k+1 == len(outlines) {
  108. drawline(outlines[k].x,outlines[k].y,4,1,w,m)
  109. }
  110. if status == 0 && same == 0 {
  111. drawline(v.x,v.y,4,0,w,m)
  112. same, status = v.x,1
  113. continue
  114. }
  115. if v.x - same == w {
  116. same, status= v.x,1
  117. }
  118. if (v.x - same > w || v.y != outlines[k-1].y ) && status == 1{
  119. drawline(outlines[k-1].x,outlines[k-1].y,4,1,w,m)
  120. same,status = 0, 0
  121. }
  122. }
  123. for _,v := range outlines{
  124. m.Set(v.x,v.y,img2.At(v.x,v.y))
  125. }
  126. jpeg.Encode(imgw, m, &jpeg.Options{100})
  127. defer imgw.Close()
  128. t3:= time.Now().Nanosecond() -t2
  129. 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))
  130. fmt.Printf("图片宽 %d,高 %d 像素. 程序运行耗时 %d 毫秒. 相片有 %d 像素不同 \n",width,high,t3/1e6,len(outlines))
  131. }
  132. //由点划线
  133. func drawline(x, y, size, dire, zone int, m *image.RGBA) error {
  134. //x,y划线起点坐标 size线粗 dire线方向 zone对比像素大小
  135. size+=4
  136. switch dire {
  137. case 0:
  138. for dot := 4;dot < size;dot++ {
  139. for z:= 0;z < zone ;z++ {
  140. m.Set(x-dot,y+z,color.RGBA{255, 0, 0, 255})
  141. }
  142. }
  143. case 1:
  144. for dot := 4;dot < size;dot++ {
  145. for z:= 0;z < zone ;z++ {
  146. m.Set(x+dot+zone,y+z,color.RGBA{0, 255, 0, 255})
  147. }
  148. }
  149. case 2:
  150. for dot := 4;dot < size;dot++ {
  151. for z:= 0;z < zone ;z++ {
  152. m.Set(x+z,y-dot,color.RGBA{255, 0, 0, 255})
  153. }
  154. }
  155. case 3:
  156. for dot := 4;dot < size;dot++ {
  157. for z:= 0;z < zone ;z++ {
  158. m.Set(x+z,y+dot,color.RGBA{0, 255, 0, 255})
  159. }
  160. }
  161. default:
  162. return errors.New("Parameter error")
  163. }
  164. return nil
  165. }
  166. // 排序,用于框出差异,优化减少重复设置像素 切片指针传递
  167. func sortline(outlines Outlinesort) {
  168. oy,startkey := -1,0
  169. if len(outlines) > 0 {
  170. oy = outlines[0].y
  171. }
  172. var sortx Outlinesort
  173. for key,value := range outlines {
  174. if value.y != oy {
  175. sortx = outlines[startkey:key]
  176. sort.Sort(Outlinesortx(sortx))
  177. startkey,oy = key,value.y
  178. }
  179. if key == outlines.Len() {
  180. sortx = outlines[startkey:key]
  181. sort.Sort(Outlinesortx(sortx))
  182. }
  183. }
  184. }
  185. //将rgb像素转化为gray,用于对比色差
  186. func rgb2gray1px(colorImg color.Color) float64 {
  187. r, g, b, _ := colorImg.RGBA()
  188. lum := 0.299*float64(r/257) + 0.587*float64(g/257) + 0.114*float64(b/256)
  189. return lum
  190. }

代码未优化  需改进

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/103873
推荐阅读
相关标签
  

闽ICP备14008679号