惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
S
Security @ Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
A
Arctic Wolf
Webroot Blog
Webroot Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Security Latest
Security Latest
H
Heimdal Security Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
T
Tor Project blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
A
About on SuperTechFans
M
MIT News - Artificial intelligence
V
V2EX
V
Visual Studio Blog
Recorded Future
Recorded Future
博客园 - 叶小钗
F
Fortinet All Blogs
L
Lohrmann on Cybersecurity
The GitHub Blog
The GitHub Blog
博客园 - Franky
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 三生石上(FineUI控件)
I
InfoQ
SecWiki News
SecWiki News
Blog — PlanetScale
Blog — PlanetScale
Engineering at Meta
Engineering at Meta
J
Java Code Geeks
B
Blog RSS Feed
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
H
Help Net Security

博客园 - 柒零壹

Ubuntu 24.04 安装最新版podman@5.6.1 [转贴]在前端如何玩转 Word 文档 一个命令行参数解决open-webui镜像启动失败的问题(huggingface网站访问失败问题) Go Template 常用疑难知识点 从数据库中随机选取数据(基于golang,xorm) go-ElasticSearch TypedClient学习笔记 golang用pgx查询数据时如何将查询结果方便的放入Map中 go语言如何使用elastic官方客户端go-elasticsearch/v8实现数据批量更新 【转】网络常用颜文字(文字表情) 解决GoCV/OpenCV不支持中文的问题 golang中xorm自动维护表结构自动导入数据的实现 [golang] gin的中间中调用Abort方法导致的带附件的表单提交时,浏览器报net::ERR_CONNECTION_RESET错误的原因及解决方法 [golang]filepath.Glob的缺陷,不支持多级目录 浏览器中javascript简易实现json数据保存到客户端 最简单搭建前端轻量级项目开发服务 [转]Three.js做一个酷炫的城市展示可视化大屏 [转]css实现不同样式的tooltip对话框小三角 [原]升级项目到Rails7.0.3,使用自己手动方案编译打包css及js资源 puma web server如何监听所有IP地址
GoCV下实现多图片单窗口内同时显示
柒零壹 · 2023-06-27 · via 博客园 - 柒零壹

问题

OPENCV的IMShow一次只能显示一张图片,但是很多时候我们需要同时显示多张图片。

方案一

网上搜索解决方案,多数是基于Python的,要么用numpy的hstack/vstack,要么使用plt解决。所幸,在opencv函数中找到了hconcat和vconcat,但是hconcat需要图片高度一致,vconcat需要图片宽度一致。我这里选用hconcat,以第一张图片的高度为基准,其他图片resize到同样高度,但是为了图片不变形,宽度要按照高度变化比例缩放。

 func ShowImages(title string,imgs ...cv.Mat,)*cv.Window{
   if len(imgs)==0{
     fmt.Println("[showImg] should give me at least one image!")
     return nil
   }
   img, width, height:=StackImagesHV(imgs...)  
   defer func(img cv.Mat){   
     if img.Size()[0]!=imgs[0].Size()[0]{ img.Close()  }
   }(img)
   win:=cv.NewWindow(title)
   win.SetWindowTitle(title)
   win.ResizeWindow(width, height )
   //
   win.IMShow(img)
   return win
 }
 func StackImagesH(imgs ...cv.Mat) (img cv.Mat, width int, height int){
   if len(imgs)==0{
     panic("[StackImagesH] should give me at least one image!")
   }
   img=cv.NewMat() 
   if len(imgs)==1{    
     img = imgs[0].Clone()
     width,height=ImageSize(img)
     return
   }
   
   imgs[0].CopyTo(&img)    
   width,height = ImageSize(imgs[0])
 for i:=1;i<len(imgs);i++{
     pic:=imgs[i]    
     tw,th:=ImageSize(pic)   
     if th!=height {
       np,nw:=ResizeImageByHeight(pic, height)     
       width += nw
       cv.Hconcat(img,  np ,&img)
       np.Close()
       
     }else{ 
       cv.Hconcat(img,  pic ,&img)
       width += tw
     }
   }
 return img,width,height
 }

方案二

方案一的解决方案有明显的局限性,由于只能单纯的横向或者纵向拼接,当图片比较多的时候,明显不太美观实用。经过一段时间思考想到了自己手动拼接图片,按照先行后列的方式,生成一个大图片,而且自己还可以定义行列的间隔(当然,方案一中也是可以通过生成空白图片的方式添加图片间隔的),不废话上代码。

 func StackImagesHV(imgs ...cv.Mat) (bigImg cv.Mat, colWidth int, rowHeight int){  
   interval:= 4 // 图片间的间隔
   imgCount:=len(imgs)
   if imgCount<=4 { return StackImagesH(imgs...)}
   colCount:=4
   if imgCount<7{
     colCount= imgCount/2 + imgCount%2
   }else if imgCount%3==0{
     colCount = imgCount/3
   }
   rowCount:= imgCount / colCount 
   if imgCount % colCount>0{
     rowCount +=1
   } 
   firstImg:=imgs[0]
   _,rowHeight=ImageSize(firstImg) 
   newImgs:=make([]cv.Mat,len(imgs))
   defer func(){
     for _,np:=range newImgs{
       np.Close()
     } 
   }()
   totalHeight:=rowCount*rowHeight + interval*(rowCount-1)
   colWidth=0   // set to max width images' width
   for row:=0;row<rowCount;row++{    
     for col:=0;col<colCount;col++{
       i:=row*colCount+ col
       if i>=imgCount{ break; }
       np,w:=ResizeImageByHeight(imgs[i],rowHeight)
       newImgs[i]=np     
       if w>colWidth{
         colWidth = w
       }
     }
   }
   totalWidth:=colWidth* colCount  + interval*(colCount-1)
   //
   fillColor:= cv.Scalar{Val1:255,Val2:0,Val3:0,Val4:0xff} // blue, for opencv default BGR color space
   bigImg= cv.NewMatWithSizeFromScalar(fillColor, totalHeight,totalWidth,firstImg.Type())
   // loop region copy
   for row:=0;row<rowCount;row++{
     left:=0
     top:=row*rowHeight + row*interval
     for col:=0; col<colCount; col++{
       i:=row*colCount+ col
       if i>=imgCount{ break; }
       np:=newImgs[i]
       picW,_:=ImageSize(np)                 
       rt:=image.Rect(left, top, left+picW, top+rowHeight )
       left += colWidth + interval
       tmpImg:= bigImg.Region(rt)
       np.CopyTo(&tmpImg)
   }
   return bigImg, totalWidth, totalHeight
 }
 

效果图