Skip to content

Commit

Permalink
Update tutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
Justin McAllister committed Apr 9, 2019
1 parent 49988e3 commit 1ddb949
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 176 deletions.
145 changes: 101 additions & 44 deletions Alignment-RigRelatives.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@
" img_type = \"reflectance\"\n",
" capture.plot_undistorted_reflectance(panel_irradiance)\n",
"else:\n",
" img_type = \"radiance\"\n",
" capture.plot_undistorted_radiance()"
" if False: #capture.dls_present():\n",
" img_type='reflectance'\n",
" capture.plot_undistorted_reflectance(capture.dls_irradiance())\n",
" else:\n",
" img_type = \"radiance\"\n",
" capture.plot_undistorted_radiance() "
]
},
{
Expand All @@ -87,27 +91,28 @@
},
"outputs": [],
"source": [
"import micasense.imageutils as imageutils\n",
"import micasense.capture\n",
"import cv2\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import micasense.imageutils as imageutils\n",
"import micasense.plotutils as plotutils\n",
"\n",
"\n",
"warp_mode = cv2.MOTION_HOMOGRAPHY\n",
"warp_matrices = capture.get_warp_matrices()\n",
"\n",
"cropped_dimensions,edges = imageutils.find_crop_bounds(capture,warp_matrices)\n",
"im_aligned = imageutils.aligned_capture(capture, warp_matrices, cv2.MOTION_HOMOGRAPHY, cropped_dimensions, None, img_type=img_type)\n",
"im_aligned = imageutils.aligned_capture(capture, warp_matrices, warp_mode, cropped_dimensions, None, img_type=img_type)\n",
"\n",
"for i,mat in enumerate(warp_matrices):\n",
" print(\"Band {}:\\n{}\".format(i,mat))"
"print(\"warp_matrices={}\".format(warp_matrices))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Image Enhancement\n",
"## Visualize Aligned Images\n",
"\n",
"There are many techniques for image enhancement, but one which is commonly used to improve the visual sharpness of imagery is the unsharp mask. Here we apply an unsharp mask to the RGB image to improve the visualization, and then apply a gamma curve to make the darkest areas brighter."
"Once the transformation has been found, it can be verified by composting the aligned images to check alignment. The image 'stack' containing all bands can also be exported to a multi-band TIFF file for viewing in extrernal software such as QGIS. Usef ul componsites are a naturally colored RGB as well as color infrared, or CIR."
]
},
{
Expand All @@ -116,18 +121,56 @@
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"rgb_band_indices = [2,1,0]\n",
"cir_band_indices = [3,2,1]\n",
"\n",
"# Create an empty normalized stack for viewing\n",
"im_display = np.zeros((im_aligned.shape[0],im_aligned.shape[1],capture.num_bands+1), dtype=np.float32 )\n",
"\n",
"im_min = np.percentile(im_aligned[:,:,0:2].flatten(), 0.5) # modify with these percentilse to adjust contrast\n",
"im_min = np.percentile(im_aligned[:,:,0:2].flatten(), 0.1) # modify with these percentilse to adjust contrast\n",
"im_max = np.percentile(im_aligned[:,:,0:2].flatten(), 99.9) # for many images, 0.5 and 99.5 are good values\n",
"for i in range(0,im_aligned.shape[2]):\n",
" im_display[:,:,i] = imageutils.normalize(im_aligned[:,:,i], im_min, im_max)\n",
"\n",
"rgb = im_display[:,:,[2,1,0]]\n",
"for i in range(0,im_aligned.shape[2]):\n",
" if img_type == 'reflectance':\n",
" # for reflectance images we maintain white-balance by applying the same display scaling to all bands\n",
" im_display[:,:,i] = imageutils.normalize(im_aligned[:,:,i], im_min, im_max)\n",
" elif img_type == 'radiance':\n",
" # for radiance images we do an auto white balance since we don't know the input light spectrum by\n",
" # stretching each display band histogram to it's own min and max\n",
" im_display[:,:,i] = imageutils.normalize(im_aligned[:,:,i])\n",
"\n",
"rgb = im_display[:,:,rgb_band_indices]\n",
"# for cir false color imagery, we normalize the NIR,R,G bands within themselves, which provides\n",
"# the classical CIR rendering where plants are red and soil takes on a blue tint\n",
"for i in cir_band_indices:\n",
" im_display[:,:,i] = imageutils.normalize(im_aligned[:,:,i])\n",
"\n",
"cir = im_display[:,:,cir_band_indices]\n",
"fig, axes = plt.subplots(1, 2, figsize=(16,16))\n",
"axes[0].set_title(\"Red-Green-Blue Composite\")\n",
"axes[0].imshow(rgb)\n",
"axes[1].set_title(\"Color Infrared (CIR) Composite\")\n",
"axes[1].imshow(cir)\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Image Enhancement\n",
"\n",
"There are many techniques for image enhancement, but one which is commonly used to improve the visual sharpness of imagery is the unsharp mask. Here we apply an unsharp mask to the RGB image to improve the visualization, and then apply a gamma curve to make the darkest areas brighter."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"# Create an enhanced version of the RGB render using an unsharp mask\n",
"gaussian_rgb = cv2.GaussianBlur(rgb, (9,9), 10.0)\n",
"gaussian_rgb[gaussian_rgb<0] = 0\n",
Expand Down Expand Up @@ -183,24 +226,37 @@
"from osgeo import gdal, gdal_array\n",
"rows, cols, bands = im_display.shape\n",
"driver = gdal.GetDriverByName('GTiff')\n",
"outRaster = driver.Create(\"bgrnet.tiff\", cols, rows, 6, gdal.GDT_UInt16)\n",
"filename = \"bgrne\" #blue,green,red,nir,redEdge\n",
"\n",
"normalize = panelCap is None\n",
"if im_aligned.shape[2] == 6:\n",
" filename = filename + \"t\" #thermal\n",
"outRaster = driver.Create(filename+\".tiff\", cols, rows, im_aligned.shape[2], gdal.GDT_UInt16)\n",
"\n",
"normalize = (img_type == 'radiance') # normalize radiance images to fit with in UInt16\n",
"\n",
"# Output a 'stack' in the same band order as RedEdge/Alutm\n",
"# Blue,Green,Red,NIR,RedEdge[,Thermal]\n",
"# reflectance stacks are output with 32768=100% reflectance to provide some overhead for specular reflections\n",
"# radiance stacks are output with 65535=100% radiance to provide some overhead for specular reflections\n",
"\n",
"# NOTE: NIR and RedEdge are not in wavelength order!\n",
"\n",
"multispec_min = np.min(im_aligned[:,:,1:5])\n",
"multispec_max = np.max(im_aligned[:,:,1:5])\n",
"\n",
"for i in range(0,5):\n",
" outband = outRaster.GetRasterBand(i+1)\n",
" if normalize:\n",
" outband.WriteArray(imageutils.normalize(im_aligned[:,:,i])*65535)\n",
" outdata = imageutils.normalize(im_aligned[:,:,i],multispec_min,multispec_max)\n",
" else:\n",
" outdata = im_aligned[:,:,i]\n",
" outdata[outdata<0] = 0\n",
" outdata[outdata>1] = 1\n",
" outband.WriteArray(outdata*65535)\n",
" outdata[outdata>2] = 2\n",
" \n",
" outdata = outdata*32767\n",
" outdata[outdata<0] = 0\n",
" outdata[outdata>65535] = 65535\n",
" outband.WriteArray(outdata)\n",
" outband.FlushCache()\n",
"\n",
"if im_aligned.shape[2] == 6:\n",
Expand Down Expand Up @@ -261,18 +317,25 @@
"\n",
"# remove shadowed areas (mask pixels with NIR reflectance < 20%))\n",
"if img_type == 'reflectance':\n",
" ndvi = np.ma.masked_where(im_aligned[:,:,3] < 0.20, ndvi)\n",
"\n",
" ndvi = np.ma.masked_where(im_aligned[:,:,3] < 0.20, ndvi) \n",
"elif img_type == 'radiance':\n",
" lower_pct_radiance = np.percentile(im_aligned[:,:,3], 10.0)\n",
" ndvi = np.ma.masked_where(im_aligned[:,:,3] < lower_pct_radiance, ndvi) \n",
" \n",
"# Compute and display a histogram\n",
"hist_min = np.min(ndvi)\n",
"hist_max = np.max(ndvi)\n",
"ndvi_hist_min = np.min(ndvi)\n",
"ndvi_hist_max = np.max(ndvi)\n",
"fig, axis = plt.subplots(1, 1, figsize=(10,4))\n",
"axis.hist(ndvi.ravel(), bins=512, range=(hist_min, hist_max))\n",
"axis.hist(ndvi.ravel(), bins=512, range=(ndvi_hist_min, ndvi_hist_max))\n",
"plt.title(\"NDVI Histogram\")\n",
"plt.show()\n",
"\n",
"min_display_ndvi = 0.45\n",
"max_display_ndvi = 0.85\n",
"min_display_ndvi = 0.45 # mask soil by removing low-ndvi values\n",
"#min_display_ndvi = np.percentile(ndvi.flatten(), 5.0) # modify with these percentilse to adjust contrast\n",
"max_display_ndvi = np.percentile(ndvi.flatten(), 99.5) # for many images, 0.5 and 99.5 are good values\n",
"\n",
"# min_display_ndvi = 0.45\n",
"# max_display_ndvi = 0.85\n",
"masked_ndvi = np.ma.masked_where(ndvi < min_display_ndvi, ndvi)\n",
"fig, axis = plotutils.plot_overlay_withcolorbar(gamma_corr_rgb, \n",
" masked_ndvi, \n",
Expand Down Expand Up @@ -303,14 +366,14 @@
"# Compute Normalized Difference Red Edge Index from the NIR(3) and RedEdge(4) bands\n",
"ndre = (im_aligned[:,:,3] - im_aligned[:,:,4]) / (im_aligned[:,:,3] + im_aligned[:,:,4])\n",
"\n",
"# Mask areas with low NDRE and low NDVI\n",
"masked_ndre = np.ma.masked_where(ndvi < 0.45, ndre)\n",
"# Mask areas with shadows and low NDVI to remove soil\n",
"masked_ndre = np.ma.masked_where(ndvi < min_display_ndvi, ndre)\n",
"\n",
"# Compute a histogram\n",
"hist_min = np.min(masked_ndre)\n",
"hist_max = np.max(masked_ndre)\n",
"ndre_hist_min = np.min(masked_ndre)\n",
"ndre_hist_max = np.max(masked_ndre)\n",
"fig, axis = plt.subplots(1, 1, figsize=(10,4))\n",
"axis.hist(masked_ndre.ravel(), bins=512, range=(hist_min, hist_max))\n",
"axis.hist(masked_ndre.ravel(), bins=512, range=(ndre_hist_min, ndre_hist_max))\n",
"plt.title(\"NDRE Histogram (filtered to only plants)\")\n",
"plt.show()\n",
"\n",
Expand Down Expand Up @@ -346,26 +409,20 @@
"source": [
"if im_aligned.shape[2] >= 5:\n",
"\n",
" thermal = im_aligned[:,:,5]\n",
"\n",
" # Mask areas with low NDRE and low NDVI\n",
" masked_thermal = thermal\n",
" # Alternatively we can mask the thermal only to plants here\n",
" # masked_thermal = np.ma.masked_where(ndvi < 0.45, thermal)\n",
" # by default we don't mask the thermal, since it's native resolution is much lower than the MS\n",
" masked_thermal = im_aligned[:,:,5]\n",
" # Alternatively we can mask the thermal only to plants here, which is useful for large contiguous areas\n",
" # masked_thermal = np.ma.masked_where(ndvi < 0.45, im_aligned[:,:,5])\n",
"\n",
"\n",
" # Compute a histogram\n",
" hist_min = np.percentile(masked_thermal, 1)\n",
" hist_max = np.percentile(masked_thermal, 99)\n",
" fig, axis = plt.subplots(1, 1, figsize=(10,4))\n",
" axis.hist(masked_thermal.ravel(), bins=512, range=(np.min(masked_thermal), np.max(masked_thermal)))\n",
" plt.title(\"Thermal Histogram\")\n",
" plt.show()\n",
"\n",
" print(hist_min)\n",
" print(hist_max)\n",
" min_display_therm = hist_min\n",
" max_display_therm = hist_max\n",
" min_display_therm = np.percentile(masked_thermal, 1)\n",
" max_display_therm = np.percentile(masked_thermal, 99)\n",
"\n",
" fig, axis = plotutils.plot_overlay_withcolorbar(gamma_corr_rgb,\n",
" masked_thermal, \n",
Expand Down
Loading

0 comments on commit 1ddb949

Please sign in to comment.