ODM icon indicating copy to clipboard operation
ODM copied to clipboard

BUG: GCP names are wrong in ODM Report

Open bugracoskun opened this issue 3 years ago • 4 comments

Hello, i have used gcp points in my process. The gcp names was like; ykn1 , ykn2, ykn3 … My GCP file is like that: +proj=tmerc +lat_0=0 +lon_0=27 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 518876.902 4453173.463 262.223 2451 542 100_0030_0001.JPG YKN6 518876.902 4453173.463 262.223 2598 1307 100_0030_0002.JPG YKN6 518838.142 4453136.888 258.616 2485 458 100_0030_0003.JPG YKN4 518876.902 4453173.463 262.223 2584 1917 100_0030_0003.JPG YKN6 518838.142 4453136.888 258.616 2461 1058 100_0030_0004.JPG YKN4 518876.902 4453173.463 262.223 2558 2535 100_0030_0004.JPG YKN6 518838.142 4453136.888 258.616 2436 1671 100_0030_0005.JPG YKN4 518876.902 4453173.463 262.223 2530 3166 100_0030_0005.JPG YKN6 518838.142 4453136.888 258.616 2409 2274 100_0030_0006.JPG YKN4 518735.306 4453072.649 259.396 200 1681 100_0030_0007.JPG YKN1 518838.142 4453136.888 258.616 2381 2881 100_0030_0007.JPG YKN4 518735.306 4453072.649 259.396 2785 182 100_0030_0008.JPG YKN1 518846.605 4453059.822 279.853 163 2406 100_0030_0008.JPG YKN2 ... So on

After the process, GCP names are shown different according to my GCP file. Like gcp0, gcp1, gcp2… image

It makes a confusing situation. Thank you

bugracoskun avatar Mar 11 '22 18:03 bugracoskun

Any progress with this bug ?

bugracoskun avatar Aug 25 '22 08:08 bugracoskun

We haven't fixed it yet (PRs would be most welcome of course). 🙏

pierotofy avatar Aug 25 '22 17:08 pierotofy

I worked on the bug and i find the solution. You can search the differences with 'CHANGED' key

Actually it is the bug of OpenSFM. The io.py file includes '_read_gcp_list_lines' function. I fixed the function like this:

def _read_gcp_list_lines(
    lines: Iterable[str],
    projection,
    exif: Dict[str, Dict[str, Any]],
) -> List[pymap.GroundControlPoint]:
    points = {}
    for line in lines:
        words = line.split(None) // CHANGED - split whole line just not for 5 column
        easting, northing, alt, pixel_x, pixel_y = map(float, words[:5])
        shot_id = words[5].strip()
        key = (easting, northing, alt)

        if key in points:
            point = points[key]
        else:
            # Convert 3D coordinates
            if np.isnan(alt):
                alt = 0
                has_altitude = False
            else:
                has_altitude = True
            if projection is not None:
                lon, lat = projection(easting, northing, inverse=True)
            else:
                lon, lat = easting, northing

            point = pymap.GroundControlPoint()
            
            // CHANGED - Added if else. If user defined name exists control.
            if(len(words) > 6):
                point.id = words[6].strip()
            else:
                point.id = "GCP-%d" % len(points)
            
            point.lla = {"latitude": lat, "longitude": lon, "altitude": alt}
            point.has_altitude = has_altitude

            points[key] = point

        # Convert 2D coordinates
        d = exif[shot_id]
        coordinates = features.normalized_image_coordinates(
            np.array([[pixel_x, pixel_y]]), d["width"], d["height"]
        )[0]

        o = pymap.GroundControlPointObservation()
        o.shot_id = shot_id
        o.projection = coordinates
        point.add_observation(o)

    return list(points.values())

I will create a pr for OpenSFM.

Also it has to be changed types.py in openDM folder contains function with name 'georeference_with_gcp'. I changed the function like this:

def georeference_with_gcp(self, gcp_file, output_coords_file, output_gcp_file, output_model_txt_geo, rerun=False):
        if not io.file_exists(output_coords_file) or not io.file_exists(output_gcp_file) or rerun:
            gcp = GCPFile(gcp_file)
            if gcp.exists():
                if gcp.entries_count() == 0:
                    raise RuntimeError("This GCP file does not have any entries. Are the entries entered in the proper format?")

                # Convert GCP file to a UTM projection since the rest of the pipeline
                # does not handle other SRS well.
                rejected_entries = []
                // CHANGED - include_extras parameters set true
                utm_gcp = GCPFile(gcp.create_utm_copy(output_gcp_file, filenames=[p.filename for p in self.photos], rejected_entries=rejected_entries, include_extras=True)) 
                
                if not utm_gcp.exists():
                    raise RuntimeError("Could not project GCP file to UTM. Please double check your GCP file for mistakes.")
                
                for re in rejected_entries:
                    log.ODM_WARNING("GCP line ignored (image not found): %s" % str(re))
                
                if utm_gcp.entries_count() > 0:
                    log.ODM_INFO("%s GCP points will be used for georeferencing" % utm_gcp.entries_count())
                else:
                    raise RuntimeError("A GCP file was provided, but no valid GCP entries could be used. Note that the GCP file is case sensitive (\".JPG\" is not the same as \".jpg\").")
                
                self.gcp = utm_gcp

                # Compute RTC offsets from GCP points
                x_pos = [p.x for p in utm_gcp.iter_entries()]
                y_pos = [p.y for p in utm_gcp.iter_entries()]
                x_off, y_off = int(np.round(np.mean(x_pos))), int(np.round(np.mean(y_pos)))

                # Create coords file, we'll be using this later
                # during georeferencing
                with open(output_coords_file, 'w') as f:
                    coords_header = gcp.wgs84_utm_zone()
                    f.write(coords_header + "\n")
                    f.write("{} {}\n".format(x_off, y_off))
                    log.ODM_INFO("Generated coords file from GCP: %s" % coords_header)
                
                # Deprecated: This is mostly for backward compatibility and should be
                # be removed at some point
                shutil.copyfile(output_coords_file, output_model_txt_geo)
                log.ODM_INFO("Wrote %s" % output_model_txt_geo)
            else:
                log.ODM_WARNING("GCP file does not exist: %s" % gcp_file)
                return
        else:
            log.ODM_INFO("Coordinates file already exist: %s" % output_coords_file)
            log.ODM_INFO("GCP file already exist: %s" % output_gcp_file)
            self.gcp = GCPFile(output_gcp_file)
        
        self.georef = ODM_GeoRef.FromCoordsFile(output_coords_file)
        return self.georef

include_extras parameters set as true. Now it works fine.

I can create a pr for this but it must be applied with the OpenSFM changes.

and the result:

image

It works !

best regards

bugracoskun avatar Aug 26 '22 08:08 bugracoskun

This is awesome! Yes a PR in OpenSfM would be the first step; you can also open it on our fork at https://github.com/OpenDroneMap/OpenSfM targeting the 288 branch if it takes too long.

pierotofy avatar Aug 26 '22 09:08 pierotofy

Did this make it's way in? Looking at pull requests, I think maybe it didn't, but it's Friday, so I'm not sure if I can still spell my name.

smathermather avatar Dec 17 '22 00:12 smathermather

I don't think it did.

pierotofy avatar Dec 17 '22 02:12 pierotofy

Brought these changes with the latest version, thanks!

pierotofy avatar Jan 09 '23 20:01 pierotofy