天天看點

基于arcgis的遙感影像标簽制作(目标檢測)

文章目錄

  • ​​1. 在arcgis中建立線矢量​​
  • ​​2. 檢測框繪制​​
  • ​​3. 檢測框坐标轉換(線矢量)​​
  • ​​4. 檢測框坐标轉換(面矢量)​​
  • ​​5. 檢測框成果轉換​​

1. 在arcgis中建立線矢量

建立線矢量,添加空間參考,例如wgs_1984。

基于arcgis的遙感影像标簽制作(目标檢測)

2. 檢測框繪制

基于arcgis的遙感影像标簽制作(目标檢測)
基于arcgis的遙感影像标簽制作(目标檢測)

3. 檢測框坐标轉換(線矢量)

地理坐标轉像素坐标

# -*- coding: utf-8 -*-
from osgeo import ogr
from osgeo import gdal
from osgeo import osr
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt


def getSRSPair(dataset):
    '''
    獲得給定資料的投影參考系和地理參考系
    :param dataset: GDAL地理資料
    :return: 投影參考系和地理參考系
    '''
    prosrs = osr.SpatialReference()
    prosrs.ImportFromWkt(dataset.GetProjection())
    geosrs = prosrs.CloneGeogCS()
    return prosrs, geosrs


def geo2lonlat(dataset, x, y):
    '''
    将投影坐标轉為經緯度坐标(具體的投影坐标系由給定資料确定)
    :param dataset: GDAL地理資料
    :param x: 投影坐标x
    :param y: 投影坐标y
    :return: 投影坐标(x, y)對應的經緯度坐标(lon, lat)
    '''
    prosrs, geosrs = getSRSPair(dataset)
    ct = osr.CoordinateTransformation(prosrs, geosrs)
    coords = ct.TransformPoint(x, y)
    return coords[:2]


def lonlat2geo(dataset, lon, lat):
    '''
    将經緯度坐标轉為投影坐标(具體的投影坐标系由給定資料确定)
    :param dataset: GDAL地理資料
    :param lon: 地理坐标lon經度
    :param lat: 地理坐标lat緯度
    :return: 經緯度坐标(lon, lat)對應的投影坐标
    '''
    prosrs, geosrs = getSRSPair(dataset)
    ct = osr.CoordinateTransformation(geosrs, prosrs)
    coords = ct.TransformPoint(lon, lat)
    return coords[:2]


def imagexy2geo(dataset, row, col):
    '''
    根據GDAL的六參數模型将影像圖上坐标(行列号)轉為投影坐标或地理坐标(根據具體資料的坐标系統轉換)
    :param dataset: GDAL地理資料
    :param row: 像素的行号
    :param col: 像素的列号
    :return: 行列号(row, col)對應的投影坐标或地理坐标(x, y)
    '''
    trans = dataset.GetGeoTransform()
    px = trans[0] + col * trans[1] + row * trans[2]
    py = trans[3] + col * trans[4] + row * trans[5]
    return px, py


def geo2imagexy(dataset, x, y):
    '''
    根據GDAL的六 參數模型将給定的投影或地理坐标轉為影像圖上坐标(行列号)
    :param dataset: GDAL地理資料
    :param x: 投影或地理坐标x
    :param y: 投影或地理坐标y
    :return: 影坐标或地理坐标(x, y)對應的影像圖上行列号(row, col)
    '''
    trans = dataset.GetGeoTransform()
    a = np.array([[trans[1], trans[2]], [trans[4], trans[5]]])
    b = np.array([x - trans[0], y - trans[3]])
    return np.linalg.solve(a, b)  # 使用numpy的linalg.solve進行二進制一次方程的求解


def main(imgPath, shpPath):
    dataset = gdal.Open(imgPath)
    ds = ogr.Open(shpPath,1)
    if ds is None:
        print('Could not open folder')
    in_lyr = ds.GetLayer()

    lyr_dn = in_lyr.GetLayerDefn()
    cls_index = lyr_dn.GetFieldIndex("Id")
    cls_name_g = lyr_dn.GetFieldDefn(cls_index)
    feature = in_lyr.GetNextFeature()

    fieldName = cls_name_g.GetNameRef()

    finalResult = []
    while feature is not None:
        geom = feature.geometry()
        # ff = feature.GetGeometry(geom)
        # print(geom)
        print('------------')
        result = []
        for i in range(0, geom.GetPointCount()-1):
            pt = np.array(geom.GetPoint(i))
            coords = lonlat2geo(dataset, pt[0], pt[1])
            coords = geo2imagexy(dataset, coords[0], coords[1])
            result.append([coords[0], coords[1]])
            # print(pt, x, y)

        finalResult.append(result)
        feature = in_lyr.GetNextFeature()

    return finalResult


if __name__ == '__main__':
    img_filename = './test/0000000004_image.tif'
    dst_filename = './test/label2.shp'
    finalResult = main(img_filename, dst_filename)
    img = cv.imread(img_filename, cv.IMREAD_LOAD_GDAL)
    finalResult = np.array(finalResult)
    for bbox in finalResult:
        xmin = min(bbox[0][0], bbox[1][0], bbox[2][0], bbox[3][0])
        ymin = min(bbox[0][1], bbox[1][1], bbox[2][1], bbox[3][1])
        xmax = max(bbox[0][0], bbox[1][0], bbox[2][0], bbox[3][0])
        ymax = max(bbox[0][1], bbox[1][1], bbox[2][1], bbox[3][1])
        cv.rectangle(img, (int(xmin), int(ymin)), (int(xmax), int(ymax)), (0, 100, 255), 5)

    plt.imshow(img)
    plt.show()      
基于arcgis的遙感影像标簽制作(目标檢測)

4. 檢測框坐标轉換(面矢量)

大部分代碼一樣,就是main函數有點差別。

def main(imgPath, shpPath):
    dataset = gdal.Open(imgPath)
    ds = ogr.Open(shpPath,1)
    if ds is None:
        print('Could not open folder')
    in_lyr = ds.GetLayer()

    lyr_dn = in_lyr.GetLayerDefn()
    cls_index = lyr_dn.GetFieldIndex("Id")
    cls_name_g = lyr_dn.GetFieldDefn(cls_index)
    feature = in_lyr.GetNextFeature()

    fieldName = cls_name_g.GetNameRef()

    finalResult = []
    while feature is not None:
        geom = feature.geometry()
        arr = np.array(feature.GetGeometryRef().GetEnvelope())
        print(arr)
        coordsMin = lonlat2geo(dataset, arr[0], arr[3])
        coordsMin = geo2imagexy(dataset, coordsMin[0], coordsMin[1])
        coordsMax = lonlat2geo(dataset, arr[1], arr[2])
        coordsMax = geo2imagexy(dataset, coordsMax[0], coordsMax[1])
        finalResult.append([coordsMin[0], coordsMin[1], coordsMax[0], coordsMax[1]])
        # ff = feature.GetGeometry(geom)
        # print(geom)
        # print('------------')
        # result = []
        # print(geom.GetPointCount())
        # for i in range(0, geom.GetPointCount()-1):
        #     pt = np.array(geom.GetPoint(i))
        #     coords = lonlat2geo(dataset, pt[0], pt[1])
        #     coords = geo2imagexy(dataset, coords[0], coords[1])
        #     result.append([coords[0], coords[1]])
            # print(pt, x, y)

        # finalResult.append(result)
        feature = in_lyr.GetNextFeature()

    return finalResult      
基于arcgis的遙感影像标簽制作(目标檢測)

5. 檢測框成果轉換

# -*- coding: utf-8 -*-
import gdal
from osgeo import ogr, osr


def read_img(filename):
    dataset=gdal.Open(filename)

    im_width = dataset.RasterXSize
    im_height = dataset.RasterYSize

    im_geotrans = dataset.GetGeoTransform()
    im_proj = dataset.GetProjection()
    im_data = dataset.ReadAsArray(0,0,im_width,im_height)

    # del dataset
    return im_width,im_height,im_proj,im_geotrans,im_data,dataset


def getSRSPair(dataset):
    '''
    獲得給定資料的投影參考系和地理參考系
    :param dataset: GDAL地理資料
    :return: 投影參考系和地理參考系
    '''
    prosrs = osr.SpatialReference()
    prosrs.ImportFromWkt(dataset.GetProjection())
    geosrs = prosrs.CloneGeogCS()
    return prosrs, geosrs


def geo2lonlat(dataset, x, y):
    '''
    将投影坐标轉為經緯度坐标(具體的投影坐标系由給定資料确定)
    :param dataset: GDAL地理資料
    :param x: 投影坐标x
    :param y: 投影坐标y
    :return: 投影坐标(x, y)對應的經緯度坐标(lon, lat)
    '''
    prosrs, geosrs = getSRSPair(dataset)
    ct = osr.CoordinateTransformation(prosrs, geosrs)
    coords = ct.TransformPoint(x, y)
    return coords[:2]


def imagexy2geo(dataset, col, row):
    '''
    根據GDAL的六參數模型将影像圖上坐标(行列号)轉為投影坐标或地理坐标(根據具體資料的坐标系統轉換)
    :param dataset: GDAL地理資料
    :param row: 像素的行号
    :param col: 像素的列号
    :return: 行列号(row, col)對應的投影坐标或地理坐标(x, y)
    '''
    trans = dataset.GetGeoTransform()
    print(trans)
    print(row,col)
    px = trans[0] + col * trans[1] + row * trans[2]
    py = trans[3] + col * trans[4] + row * trans[5]
    return px, py


def imagexy2shp(img_path, strVectorFile, bboxes):
    im_width,im_height,im_proj,im_geotrans,im_data,dataset = read_img(img_path)
    gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO")  # 為了支援中文路徑
    gdal.SetConfigOption("SHAPE_ENCODING", "CP936")  # 為了使屬性表字段支援中文
    ogr.RegisterAll()
    strDriverName = "ESRI Shapefile"  # 建立資料,這裡建立ESRI的shp檔案
    oDriver = ogr.GetDriverByName(strDriverName)
    if oDriver == None:
        print("%s 驅動不可用!\n", strDriverName)

    oDS = oDriver.CreateDataSource(strVectorFile)  # 建立資料源
    if oDS == None:
        print("建立檔案【%s】失敗!", strVectorFile)

    # srs = osr.SpatialReference()  # 建立空間參考
    # srs.ImportFromEPSG(4326)  # 定義地理坐标系WGS1984
    srs = osr.SpatialReference(wkt=dataset.GetProjection())#我在讀栅格圖的時候增加了輸出dataset,這裡就可以不用指定投影,實作全自動了,上面兩行可以注釋了,并且那個proj參數也可以去掉了,你們自己去掉吧
    papszLCO = []
    # 建立圖層,建立一個多邊形圖層,"TestPolygon"->屬性表名
    oLayer = oDS.CreateLayer("TestPolygon", srs, ogr.wkbPolygon, papszLCO)
    if oLayer == None:
        print("圖層建立失敗!\n")

    '''下面添加矢量資料,屬性表資料、矢量資料坐标'''
    oFieldID = ogr.FieldDefn("FieldID", ogr.OFTInteger)  # 建立一個叫FieldID的整型屬性
    oLayer.CreateField(oFieldID, 1)

    # oFieldName = ogr.FieldDefn("FieldName", ogr.OFTString)  # 建立一個叫FieldName的字元型屬性
    # oFieldName.SetWidth(100)  # 定義字元長度為100
    # oLayer.CreateField(oFieldName, 1)

    oDefn = oLayer.GetLayerDefn()  # 定義要素
    # 建立單個面
    for bbox in bboxes:
        oFeatureTriangle = ogr.Feature(oDefn)
        oFeatureTriangle.SetField(0, 0)  # 第一個參數表示第幾個字段,第二個參數表示字段的值
        # oFeatureTriangle.SetField(1, "單個面")
        ring = ogr.Geometry(ogr.wkbLinearRing)  # 建構幾何類型:線
        ring.AddPoint(bbox[0], bbox[1])  # 添加點01
        ring.AddPoint(bbox[2], bbox[1])  # 添加點02
        ring.AddPoint(bbox[2], bbox[3])  # 添加點03
        ring.AddPoint(bbox[0], bbox[3])  # 添加點04
        yard = ogr.Geometry(ogr.wkbPolygon)  # 建構幾何類型:多邊形
        yard.AddGeometry(ring)
        yard.CloseRings()

        geomTriangle = ogr.CreateGeometryFromWkt(str(yard))  # 将封閉後的多邊形集添加到屬性表
        oFeatureTriangle.SetGeometry(geomTriangle)
        oLayer.CreateFeature(oFeatureTriangle)

    oDS.Destroy()
    print("資料集建立完成!\n")


def parse_txt(path):
    f = open(path)
    data = f.readlines()
    anns = []
    for ann in data:
        try:
            ann = ann.split(' ')
            xmin = float(ann[0])
            ymin = float(ann[1])
            xmax = float(ann[2])
            ymax = float(ann[3])
            anns.append([xmin, ymin, xmax, ymax])
        except:
            print('error', ann)
    return anns


if __name__ == "__main__":
    img_filename = './test/0000000004_image.tif'
    dst_filename = './test/label6.shp'
    # imagexy2shp(img_filename, dst_filename)
    txtPath = './test/0000000004_image.txt'
    dataset = gdal.Open(img_filename)
    anns = parse_txt(txtPath)
    annsGEO = []
    for ann in anns:
        xmin, ymin = imagexy2geo(dataset, ann[0], ann[1])
        xmax, ymax = imagexy2geo(dataset, ann[2], ann[3])
        annsGEO.append([xmin, ymin, xmax, ymax])

    # 轉換成面矢量
    imagexy2shp(img_filename, dst_filename, annsGEO)