使用OpenCV + GDAL 拼接图片

3 篇文章 0 订阅

前言:

        之前写过一章关于Gdal批量拼接,剪切图片的博客,但是有一些局限性,如果相机拍摄的时机不一定,物体移动的时候,拍出来的图片起始高度不同,且没办法测量偏移量,就不适用了。

        所以我想到一个办法。就是在要拍摄的物体上方加一条标识线,如下图所示:

         假设物体在标识线下方,我们只需要按照标识线位置拼接就好。比如拍出来的图片如下面两张图所示:

        假设图一和图二是完美情况拍出来的图片,高度分毫不差,这样我们不用考虑偏移,正常拼接就好,但是如果遇到图三这样的,突然某一张图片起始高度变了,而且不知道偏移了多少,就比较麻烦了。普通拼接就不适合了。

        所以利用标识线的方式就能完美拼接图片了,只要保证相机能拍到标识线。

第一步:找标识线

 /// <summary>
        ///寻找标识线坐标 ,保留标识线下方的部分,裁剪多余部分
        /// </summary>
        /// <param name="filePath">图片地址</param>
        /// <param name="outFilePath">输出图片地址</param>
        private void FindPosition(string filePath,string outFilePath)
        {
            Mat backImg = new Mat(filePath, ImreadModes.AnyColor);
            Mat mat = new Mat();
            backImg.CopyTo(mat);// 作为对比
            int height = backImg.Rows;
            int width = backImg.Cols;
            for (int i = 0; i < height; i++)
            {
                bool colMate = true;
                byte pixelValue = (byte)Math.Abs(backImg.Get<byte>(i, 0));//获取每行每列像素值
               //像素值误差小于5
                if (pixelValue <= 5)
                {
                    bool rowMate = true;
                    for (int m = 0; m < width; m++)
                    {
                        byte col = (byte)Math.Abs(backImg.Get<byte>(i, m));
                        if (col >= 5)
                        {
                            rowMate = false;
                            break;
                        }
                    }
                    if (rowMate)
                    {
                        for (int j = 0; j < 2 && j + i < height; j++)
                        {
                            byte col = (byte)Math.Abs(backImg.Get<byte>(j + i, 0));
                            
                            if (col >= 5)
                            {
                                colMate = false;
                                break;
                            }
                        }
                        if (colMate)
                        {
                            CutImage(filePath, outFilePath, i);
                            break;
                        }
                    }
                }

            }
        }
        /// <summary>
        /// 裁剪图片
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="outFileName"></param>
        /// <param name="position_y">裁剪位置</param>
        private void CutImage(string filePath, string outFileName, int position_y)
        {
            GdalConfiguration.ConfigureGdal();

            GdalConfiguration.ConfigureOgr();
            Gdal.AllRegister();
            Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
            using (Dataset srcDs = Gdal.Open(filePath, Access.GA_ReadOnly))
            {
                
                DataType srcType = srcDs.GetRasterBand(1).DataType;
                int bandCount = srcDs.RasterCount;
                double[] adfGeoTransform = new double[6];
                srcDs.GetGeoTransform(adfGeoTransform);

                int width = srcDs.RasterXSize;  // 图片的宽度
                int height = srcDs.RasterYSize; // 图片的高度

                int[] dataArray = new int[width * (height - position_y) * bandCount];
               
                int[] bandArray = new int[bandCount];
                for (int i = 0; i < bandCount; i++)
                {
                    bandArray[i] = i + 1;
                }

                srcDs.ReadRaster(0, position_y, width, (height - position_y), dataArray, width, (height - position_y), bandCount, bandArray, 0, 0, 0);
                Driver drv = Gdal.GetDriverByName("BMP");
                if (File.Exists( outFileName))
                {
                    File.Delete(outFileName);
                }
                using (Dataset dstDs = drv.Create(outFileName.Replace(" ", ""), width, (height - position_y), bandCount, srcType, null))
                {
                    dstDs.WriteRaster(0, 0, width, (height - position_y), dataArray, width, (height - position_y), bandCount, bandArray, 0, 0, 0);
                    dstDs.FlushCache();
                    dstDs.Dispose();
                }

            }
        }

第二步:创建拼接图片的工具类

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using OSGeo.GDAL;

namespace OpenCVGDAL
{
    public class JigsawPuzzle
    {
        private static string _outFileName = @"E:\t.bmp";

        private static int _offset_x = 93;
        private static int _offset_y = 26;
        private static string[] _files;

        public JigsawPuzzle(int offset_x, int offset_y,string [] files,string outFileName)
        {
            _offset_x = offset_x;
            _offset_x = offset_y;
            _files = iniArray(files);
            _outFileName = outFileName;  
            
        }
        //文件名排序
        private string[] iniArray(string [] files)
        {
            Array.Sort(files);
            return files;
        }
        public void Start()
        {
         
            CombineTiles(_files);
                    
        }

        private static void SaveBitMapBuffer(Dataset srcDs, Dataset outDs, int index)
        {
            if (srcDs.RasterCount < 1)
            {
                return;
            }
            int width = srcDs.RasterXSize;
            int height = srcDs.RasterYSize;
            int bandCount = srcDs.RasterCount;
            int[] dataArray = new int[width * width * bandCount];

            int[] bandArray = new int[bandCount];
            for (int i = 0; i < bandCount; i++)
            {
                bandArray[i] = i + 1;
            }
            //byte[] buf = new byte[width * height];

            srcDs.ReadRaster(0, 0, width, height, dataArray, width, height, bandCount, bandArray, 0, 0, 0);
            outDs.WriteRaster(index * width , 0, width, height, dataArray, width, height, bandCount, bandArray, 0, 0, 0);

        }

        private static void CombineTiles(string [] fileNames)
        {
            int fileCount = fileNames.Length;
            if (fileCount < 1) 
            {
                return;
            }

            GdalConfiguration.ConfigureGdal();

            GdalConfiguration.ConfigureOgr();
            Gdal.AllRegister();
            //Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
            int maxHeight = 0;
            foreach (var item in fileNames)
            {
                using (Dataset fristDs = Gdal.Open(item, Access.GA_ReadOnly))
                {
                    int height = fristDs.RasterYSize; // 图片的高度
                    maxHeight = maxHeight < height ? height : maxHeight;
                }
            }
            using (Dataset fristDs = Gdal.Open(fileNames[0], Access.GA_ReadOnly))
            {
                DataType srcType = fristDs.GetRasterBand(1).DataType; 
                int width = fristDs.RasterXSize;  // 图片的宽度
                //合成图片的大小
                int imageWidth = width * fileCount ;
                int imageHeight = maxHeight;
                Driver driver = Gdal.GetDriverByName("BMP");
                if (File.Exists(_outFileName))
                {
                    File.Delete(_outFileName);
                }
               
                using (Dataset outDs = driver.Create(_outFileName, imageWidth, imageHeight, 3, srcType, null))
                {
                    for (int i = 0; i < fileNames.Length; i++)
                    {
                        using (Dataset srcDs = Gdal.Open(fileNames[i], Access.GA_ReadOnly))
                            SaveBitMapBuffer(srcDs, outDs, i);
                    }
                   
                }
            }
            
        }

       
    }
}

第三步:依次调用方法

 private void button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.Multiselect = true;//可以选择多个选项
            if (!(fileDialog.ShowDialog() == DialogResult.OK))
            {
                return;
            }
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "(*.bmp)|*.bmp";
            if (!(saveFileDialog.ShowDialog() == DialogResult.OK))
            {
                return;
            }
            
            string[] files = new string[fileDialog.FileNames.Length];
            for (int i = 0; i < fileDialog.FileNames.Length; i++)
            {
                files[i] = fileDialog.FileNames[i];
            }
            string[] outfile = new string[files.Length];
            for (int i = 0; i < files.Length; i++)
            {
                string item = files[i];
                string outFilePath = Path.GetDirectoryName(item)+"\\" + Path.GetFileNameWithoutExtension(item) + i + ".bmp";
                outfile[i] = outFilePath;
            }
            for (int i = 0; i < files.Length; i++)
            {
                FindPosition(files[i], outfile[i]);
            }
           
            JigsawPuzzle jigsawPuzzle = new JigsawPuzzle(0,0, outfile, saveFileDialog.FileName);
            jigsawPuzzle.Start();
        }

大功告成!展示一下拼接结果:

 总结

        博主是刚学习opencv,许多地方也不懂。比如像素值的范围问题,我试了很多次,搞不懂标识线的一行都是黑色的,但是偏偏有的值是1,有的是2,3,4。相差有点离谱。但是大多数还是0,可能是因为绝对值的原因吧,或者分辨率....。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小小陆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值