def undistort(self, rgb=True, day_only=True): """ Undistort the raw image, set rgb, red, rbr, cos_g Input: rgb and day_only flags Output: rgb, red, rbr, cos_g will be specified. """ #####get the image acquisition time, this need to be adjusted whenever the naming convention changes t_cur=datetime.strptime(self.fn[-18:-4],'%Y%m%d%H%M%S'); t_std = t_cur-timedelta(hours=5) #####adjust UTC time into local standard time sz = 90-ps.get_altitude(self.cam.lat,self.cam.lon,t_std); sz*=deg2rad; self.sz = sz if day_only and sz>85*deg2rad: return saz = 360-ps.get_azimuth(self.cam.lat,self.cam.lon,t_std); saz=(saz%360)*deg2rad; self.saz = saz try: im0=plt.imread(self.fn); except: print('Cannot read file:', self.fn) return None im0=im0[self.cam.roi] im0[~self.cam.valid0,:]=0 cos_sz=np.cos(sz) cos_g=cos_sz*np.cos(self.cam.theta0)+np.sin(sz)*np.sin(self.cam.theta0)*np.cos(self.cam.phi0-saz); red0=im0[:,:,0].astype(np.float32); red0[red0<=0]=np.nan rbr0=(red0-im0[:,:,2])/(im0[:,:,2]+red0) if np.nanmean(red0[(cos_g>0.995) & (red0>=1)])>230: mk=cos_g>0.98 red0[mk]=np.nan rbr0[mk]=np.nan rbr=st.fast_bin_average2(rbr0,self.cam.weights); rbr=st.fill_by_mean2(rbr,7, mask=(np.isnan(rbr)) & self.cam.valid) self.rbr=rbr red0-=st.rolling_mean2(red0,300,ignore=np.nan) red=st.fast_bin_average2(red0,self.cam.weights); red=st.fill_by_mean2(red,7, mask=(np.isnan(red)) & self.cam.valid) red[red>50]=50; red[red<-50]=-50 red=(red+51)*2.5+0.5; self.red=red.astype(np.uint8) if rgb: im=np.zeros((self.cam.ny,self.cam.nx,3),dtype=im0.dtype) for i in range(3): im[:,:,i]=st.fast_bin_average2(im0[:,:,i],self.cam.weights); im[:,:,i]=st.fill_by_mean2(im[:,:,i],7, ignore=0, mask=(im[:,:,i]==0) & (self.cam.valid)) # im[:,:,i]=st.fill_by_mean2(im[:,:,i],7, ignore=0, mask=np.isnan(red)) im[red<=0]=0 self.rgb=im
nopen = max(5, int(np.sqrt(vx**2 + vy**2) / 3)) cm2 = (ert > 15 * scale) & (q[-2].cm) cm2 = morphology.binary_opening(cm2, np.ones( (nopen, nopen))) ####remove line-like structures cm2 = remove_small_objects(cm2, min_size=500, connectivity=4) ####remove small objects sec_layer = np.sum(cm2) / len( cm2.ravel()) ###the amount of clouds in secondary layer if sec_layer < 5e-3: ###too few pixels, no need to process secondary cloud layer err.popleft() continue elif sec_layer > 1e-1: ####there are significant amount of secondary layer clouds, we may need to re-run pass ####the cloud motion algorithm for the dominant cloud layer by masking out the secondary layer #####obtain the mask for secondar cloud layer using a watershed-like algorithm mred = q[-2].rgb[:, :, 0].astype(np.float32) - st.fill_by_mean2( q[-2].rgb[:, :, 0], 200, mask=~cm2) mrbr = q[-2].rbr - st.fill_by_mean2(q[-2].rbr, 200, mask=~cm2) merr = st.rolling_mean2(ert, 200, ignore=np.nan) var_err = (st.rolling_mean2(ert**2, 200, ignore=np.nan) - merr**2) #mk=(np.abs(q[-2].rgb[:,:,0].astype(np.float32)-mred)<3) & ((total_err)>-2) & (np.abs(q[-2].rbr-mrbr)<0.05) mk = (np.abs(mred) < 3) & (ert > -15) & (np.abs(mrbr) < 0.05) & ( var_err > 20 * 20) cm2 = morphology.binary_opening(mk | cm2, np.ones(( nopen, nopen))) ####remove line objects produced by cloud deformation cm2 = remove_small_objects(cm2, min_size=500, connectivity=4) q[-2].cm[cm2] = 2 #####update the cloud mask with secondary cloud layer #####cloud motion for the secondary layer mask2 = np.abs(err[-2]) > 5
def undistort(self, cam, rgb=True, day_only=True): """ Undistort the raw image, set rgb, red, rbr, cos_g Input: rgb and day_only flags Output: rgb, red, rbr, cos_g will be specified. """ #####get the image acquisition time, this need to be adjusted whenever the naming convention changes #ts=localToUTCtimestamp(datetime.strptime(self.fn[-18:-4],'%Y%m%d%H%M%S'),cam.cam_tz) #get UTC timestamp #t_std=UTCtimestampTolocal(ts, pytz.timezone("UTC")) #create datetime in UTC t_std = localToUTC(datetime.strptime(self.fn[-18:-4], '%Y%m%d%H%M%S'), self.cam_tz) #t_std=datetime.strptime(self.fn[-18:-4],'%Y%m%d%H%M%S'); #t_std = t_local + timedelta(hours=5) #replace(tzinfo=timezone(-timedelta(hours=5))) #print("\tUndistortion->t_local=%s\t\tt_std=%s\n" % (str(t_local),str(t_std))) print("\tUndistortion->t_std=%s\n" % (str(t_std))) gatech = ephem.Observer() gatech.date = t_std.strftime('%Y/%m/%d %H:%M:%S') gatech.lat, gatech.lon = str(self.lat), str(self.lon) sun = ephem.Sun() sun.compute(gatech) sz = np.pi / 2 - sun.alt self.sz = sz if day_only and sz > 75 * deg2rad: print("Night time (sun angle = %f), skipping\n" % sz) return saz = 180 + sun.az / deg2rad saz = (saz % 360) * deg2rad self.saz = saz try: im0 = plt.imread(self.fn) except: print('Cannot read file:', self.fn) return None im0 = im0[cam.roi] cos_sz = np.cos(sz) cos_g = cos_sz * np.cos(cam.theta0) + np.sin(sz) * np.sin( cam.theta0) * np.cos(cam.phi0 - saz) red0 = im0[:, :, 0].astype(np.float32) red0[red0 <= 0] = np.nan # rbr0=(red0-im0[:,:,2])/(im0[:,:,2]+red0) if np.nanmean(red0[(cos_g > 0.995) & (red0 >= 1)]) > 230: mk = cos_g > 0.98 red0[mk] = np.nan xsun, ysun = np.tan(sz) * np.sin(saz), np.tan(sz) * np.cos(saz) self.sun_x, self.sun_y = int( 0.5 * self.nx * (1 + xsun / cam.max_tan)), int( 0.5 * self.ny * (1 + ysun / cam.max_tan)) invalid = ~cam.valid red = st.fast_bin_average2(red0, cam.weights) red = st.fill_by_mean2(red, 7, mask=(np.isnan(red)) & cam.valid) red[invalid] = np.nan # plt.figure(); plt.imshow(red); plt.show(); red -= st.rolling_mean2(red, int(self.nx // 6.666)) red[red > 50] = 50 red[red < -50] = -50 red = (red + 50) * 2.54 + 1 red[invalid] = 0 self.red = red.astype(np.uint8) # plt.figure(); plt.imshow(self.red); plt.show(); if rgb: im = np.zeros((self.ny, self.nx, 3), dtype=im0.dtype) for i in range(3): im[:, :, i] = st.fast_bin_average2(im0[:, :, i], cam.weights) im[:, :, i] = st.fill_by_mean2(im[:, :, i], 7, ignore=0, mask=(im[:, :, i] == 0) & (cam.valid)) # im[:,:,i]=st.fill_by_mean2(im[:,:,i],7, ignore=0, mask=np.isnan(red)) im[self.red <= 0] = 0 # plt.figure(); plt.imshow(im); plt.show() self.rgb = im
def undistort(self, rgb=True, day_only=True): """ Undistort the raw image, set rgb, red, rbr, cos_g Input: rgb and day_only flags Output: rgb, red, rbr, cos_g will be specified. """ #####get the image acquisition time, this need to be adjusted whenever the naming convention changes self.time = datetime.strptime(self.fn[-18:-4], '%Y%m%d%H%M%S') t_std = self.time - timedelta( hours=5) #####adjust UTC time into local standard time sz = 90 - ps.get_altitude(self.cam.lat, self.cam.lon, t_std) sz *= deg2rad self.sz = sz if day_only and sz > 75 * deg2rad: return saz = 360 - ps.get_azimuth(self.cam.lat, self.cam.lon, t_std) saz = (saz % 360) * deg2rad self.saz = saz try: im0 = plt.imread(self.fn) except: print('Cannot read file:', self.fn) return None im0 = im0[self.cam.roi] im0[~self.cam.valid0, :] = 0 cos_sz = np.cos(sz) cos_g = cos_sz * np.cos(self.cam.theta0) + np.sin(sz) * np.sin( self.cam.theta0) * np.cos(self.cam.phi0 - saz) red0 = im0[:, :, 0].astype(np.float32) red0[red0 <= 0] = np.nan rbr0 = (red0 - im0[:, :, 2]) / (im0[:, :, 2] + red0) if np.nanmean(red0[(cos_g > 0.995) & (red0 >= 1)]) > 230: mk = cos_g > 0.98 red0[mk] = np.nan rbr0[mk] = np.nan rbr = st.fast_bin_average2(rbr0, self.cam.weights) rbr = st.fill_by_mean2(rbr, 7, mask=(np.isnan(rbr)) & self.cam.valid) rbr[self.cam.invalid] = np.nan # if np.nanmean(rbr[rbr>-0.7])<0: # hist=np.histogram(rbr,bins=100,range=[-0.6,0.15]); bins=(hist[1][:-1]+hist[1][1:])/2 # cum=np.cumsum(hist[0]); cumi=np.cumsum(hist[0][::-1]) # x1=bins[np.argmax(cum>0.02*cum[-1])]; x2=bins[99-np.argmax(cumi>0.02*cum[-1])] # print(x1,x2,np.nanmean(rbr[rbr>-0.7])) # if (x2-x1)>=0.15 or ((x2-x1)>0.1 and x1<-0.2): # rbr=-0.6+0.75/(x2-x1)*(rbr-x1); self.rbr = rbr red0 -= st.rolling_mean2(red0, 300, ignore=np.nan) red = st.fast_bin_average2(red0, self.cam.weights) red = st.fill_by_mean2(red, 7, mask=(np.isnan(red)) & self.cam.valid) red[red > 50] = 50 red[red < -50] = -50 red = (red + 51) * 2.5 + 0.5 red[self.cam.invalid] = 0 self.red = red.astype(np.uint8) if rgb: im = np.zeros((self.cam.ny, self.cam.nx, 3), dtype=im0.dtype) for i in range(3): im[:, :, i] = st.fast_bin_average2(im0[:, :, i], self.cam.weights) im[:, :, i] = st.fill_by_mean2(im[:, :, i], 7, ignore=0, mask=(im[:, :, i] == 0) & (self.cam.valid)) # im[:,:,i]=st.fill_by_mean2(im[:,:,i],7, ignore=0, mask=np.isnan(red)) im[(red <= 0) | (self.cam.invalid)] = 0 self.rgb = im
def preprocess(camera,f,q,err,fft,convolver,flag): img=cam.image(camera,f); ###img object contains four data fields: rgb, red, rbr, and cm img.undistort(rgb=True); ###undistortion if img.red is None: return # ims = Image.fromarray(img.rgb); ims.save(outpath+camID+'/'+os.path.basename(f), "PNG"); continue img.cloud_mask(); ###one-layer cloud masking q.append(img) if len(q)<=1: return ####len(q) is always 2 beyond this point if (q[-1].time-q[-2].time).seconds>=MAX_INTERVAL: q.popleft(); return; #####cloud motion for the dominant layer # im1=q[-2].red.copy().astype(np.float32); im2=q[-1].red.copy().astype(np.float32); # vy,vx,max_corr = cam.cloud_motion(im1,im2,mask1=im1>5,mask2=np.abs(im1-im2)>15, ratio=0.7, threads=4) # print(camera.camID+', ', f[-18:-4]+', first layer2:',max_corr,vy,vx) if convolver is None: shape=(camera.nx,camera.ny) convolver = mncc.Convolver(shape, shape, threads=4, dtype=np.float32) # for ii in range(len(fft)-2,0): im=q[ii].red.astype(np.float32); mask = im>0; #im[~mask]=0 fft.append(convolver.FFT(im,mask,reverse=flag[0]>0)); flag[0] *= -1 vy,vx,max_corr = cam.cloud_motion_fft(convolver,fft[-2],fft[-1],ratio=0.8); if vx is None or vy is None: #####invalid cloud motion q.popleft(); fft.popleft(); return vy*=flag[0]; vx*=flag[0]; fft.popleft(); print(camera.camID+', ', f[-18:-4]+', first layer:',max_corr,vy,vx) # plt.figure(); plt.imshow(q[-2].red); plt.colorbar(); plt.show(); return; q[-2].v+=[[vy,vx]] red1=st.shift_2d(q[-1].rgb[:,:,0].astype(np.float32),-vx,-vy); red1[red1<=0]=np.nan red2=q[-2].rgb[:,:,0].astype(np.float32); red2[red2<=0]=np.nan #red2-=np.nanmean(red2-q[-1].rgb[:,:,0]) er=red2-red1; ###difference image after cloud motion adjustment er[(red1==0)|(red2==0)]=np.nan; a=er.copy(); a[a>0]=0; er-=st.rolling_mean2(a,500); if len(err) <= 0: err += [-st.shift_2d(er,vx,vy)] return err_2=err[0].copy(); err[0] = (-st.shift_2d(er,vx,vy)) # if vy**2+vx**2>=50**2: ######The motion of the dominant layer is fast, likely low clouds. Do NOT trigger the second layer algorithm # return #####process the secondary layer ert=er+err_2; # cm2=(er>15) | (er_p>15); cm2=remove_small_objects(cm2, min_size=500, connectivity=4); scale=red2/np.nanmean(red2); nopen=max(5,int(np.sqrt(vx**2+vy**2)/3)) cm2=(ert>15*scale) & (q[-2].cm); cm2=morphology.binary_opening(cm2,np.ones((nopen,nopen))) cm2=remove_small_objects(cm2, min_size=500, connectivity=4); # sec_layer=np.sum(cm2)/len(cm2.ravel()) ###the amount of clouds in secondary layer sec_layer=np.sum(cm2)/np.sum(q[-2].cm) ###the amount of clouds in secondary layer if sec_layer<5e-2: ###too few pixels in second layer, ignore the second cloud layer print('Second layer is small:', sec_layer*100, '%') return #####cloud motion for the secondary layer mask2=np.abs(err_2)>5; mask2=remove_small_objects(mask2, min_size=500, connectivity=4) mask2=filters.maximum_filter(mask2,20) vy,vx,max_corr = cam.cloud_motion(err[0],err_2,mask1=None,mask2=mask2, ratio=None, threads=4) if vx is None or vy is None: return q[-2].v+=[[vy,vx]] print(camera.camID+', ', f[-18:-4]+', second layer:',max_corr,vy,vx) if np.abs(vy-q[-2].v[0][0])+np.abs(vx-q[-2].v[0][1])>10: # if np.abs(vy)+np.abs(vx)>0: #####obtain the mask for secondar cloud layer using a watershed-like algorithm mred=q[-2].rgb[:,:,0].astype(np.float32)-st.fill_by_mean2(q[-2].rgb[:,:,0],200,mask=~cm2) mrbr=q[-2].rbr-st.fill_by_mean2(q[-2].rbr,200,mask=~cm2) merr=st.rolling_mean2(ert,200,ignore=np.nan); var_err=(st.rolling_mean2(ert**2,200,ignore=np.nan)-merr**2) # mk=(np.abs(q[-2].rgb[:,:,0].astype(np.float32)-mred)<3) & ((total_err)>-2) & (np.abs(q[-2].rbr-mrbr)<0.05) mk=(np.abs(mred)<3) & (ert>-15) & (np.abs(mrbr)<0.05) & (var_err>20*20) cm2=morphology.binary_opening(mk|cm2,np.ones((nopen,nopen))) ####remove line objects produced by cloud deformation cm2=remove_small_objects(cm2, min_size=500, connectivity=4) q[-2].layers=2; q[-2].cm[cm2]=2; #####update the cloud mask with secondary cloud layer # fig,ax=plt.subplots(2,2, sharex=True,sharey=True); ####visualize the cloud masking results # ax[0,0].imshow(q[-2].rgb); ax[0,1].imshow(q[-2].cm) # ax[1,0].imshow(st.shift_2d(q[-1].rgb,-vx,-vy)) # ax[1,1].imshow(er,vmin=-25,vmax=25); plt.show(); return;
theta=theta0; phi=phi0 #####correction for the mis-pointing error k=np.array((np.sin(azm),np.cos(azm),0)) a=np.array([np.sin(theta0)*np.cos(phi0),np.sin(theta0)*np.sin(phi0),np.cos(theta0)]); a = np.transpose(a,[1,2,0]) b=np.cos(beta)*a + np.sin(beta)*np.cross(k,a,axisb=2) \ + np.reshape(np.outer(np.dot(a,k),k),(ny0,nx0,3))*(1-np.cos(beta)) theta=np.arctan(np.sqrt(b[:,:,0]**2+b[:,:,1]**2)/b[:,:,2]) phi=np.arctan2(b[:,:,1],b[:,:,0])%(2*np.pi) theta_filter = (theta>max_theta) | (theta<=0); theta[theta_filter]=np.nan; #####coordinate system for the undistorted space r=np.tan(theta); x,y=r*np.sin(phi), r*np.cos(phi) for f in sorted(glob.glob(inpath+camera+'/'+day+'/'+camera+'_'+day+'*jpg')): ###8200 print(f) # ######read the image to array im0=plt.imread(f).astype(np.float32); im0=im0[ystart:ystart+ny0,xstart:xstart+nx0,:] im0[theta_filter,:]=np.nan im=np.zeros((ny,nx,3)) for i in range(3): im[:,:,i]=st.bin_average2_reg(im0[:,:,i],x,y,xbin,ybin,mask=valid); im[:,:,i]=st.fill_by_mean2(im[:,:,i],7, mask=(np.isnan(im[:,:,i])) & valid ) ims = Image.fromarray(im.astype(np.uint8)) ims.save(outpath+camera+'/'+day+'/'+os.path.basename(f)[:-3]+'png', "PNG")
cnt=0; # for f in sorted(glob.glob(inpath+camera+'*2018022117*jpg')): for f in sorted(glob.glob(inpath+camera+'*20180219171940*jpg')): # for f in sorted(glob.glob(inpath+camera+'*20180214192235*jpg')): print(f) cnt+=1; ######read the image to array im0=plt.imread(f).astype(np.float32); im0=st.shift_2d(im0,-di0,-dj0); im0=im0[:nj0,:ni0]; ####cut the appropriate subset of the original image im0[theta_filter,:]=np.nan ####perform undistortion im=np.zeros((nj,ni,3),dtype=np.float32) for ic in range(3): im[:,:,ic]=st.bin_average2_reg(im0[:,:,ic],x,y,xbin,ybin,mask=valid); im[:,:,ic]=st.fill_by_mean2(im[:,:,ic],7, mask=(np.isnan(im[:,:,ic])) & valid ) im0=st.shift_2d(im0,5,0); im2=np.zeros((nj,ni,3),dtype=np.float32) for ic in range(3): im2[:,:,ic]=st.bin_average2_reg(im0[:,:,ic],x2,y2,xbin,ybin,mask=valid); im2[:,:,ic]=st.fill_by_mean2(im2[:,:,ic],7, mask=(np.isnan(im2[:,:,ic])) & valid ) plt.figure(); plt.imshow(im/255); ###undistored image plt.figure(); plt.imshow(im2/255); ###undistored image
def undistort(self, cam, rgb=True, day_only=True): """ Undistort the raw image, set rgb, red, rbr, cos_g Input: rgb and day_only flags Output: rgb, red, rbr, cos_g will be specified. """ #####get the image acquisition time, this need to be adjusted whenever the naming convention changes self.time=datetime.strptime(self.fn[-18:-4],'%Y%m%d%H%M%S'); gatech = ephem.Observer(); gatech.date = self.time.strftime('%Y/%m/%d %H:%M:%S') gatech.lat, gatech.lon = str(self.lat),str(self.lon) sun=ephem.Sun() ; sun.compute(gatech); sz = np.pi/2-sun.alt; self.sz = sz if day_only and sz>75*deg2rad: return saz = 180+sun.az/deg2rad; saz=(saz%360)*deg2rad; self.saz = saz try: im0=plt.imread(self.fn); except: print('Cannot read file:', self.fn) return None im0=im0[cam.roi] cos_sz=np.cos(sz) cos_g=cos_sz*np.cos(cam.theta0)+np.sin(sz)*np.sin(cam.theta0)*np.cos(cam.phi0-saz); red0=im0[:,:,0].astype(np.float32); red0[red0<=0]=np.nan rbr0=(red0-im0[:,:,2])/(im0[:,:,2]+red0) if np.nanmean(red0[(cos_g>0.995) & (red0>=1)])>30: mk=cos_g>0.98 red0[mk]=np.nan rbr0[mk]=np.nan xsun, ysun = np.tan(sz)*np.sin(saz), np.tan(sz)*np.cos(saz) self.sun_x,self.sun_y = int(0.5*self.nx*(1+xsun/cam.max_tan)), int(0.5*self.ny*(1+ysun/cam.max_tan)) invalid=~cam.valid rbr=st.fast_bin_average2(rbr0,cam.weights); rbr=st.fill_by_mean2(rbr,7, mask=(np.isnan(rbr)) & cam.valid) rbr[invalid]=np.nan rbr -= st.rolling_mean2(rbr,int(self.nx//6.666),ignore=np.nan) rbr[rbr>0.08]=0.08; rbr[rbr<-0.08]=-0.08; rbr=(rbr+0.08)*1587.5+1; rbr[invalid]=0 self.rbr=rbr.astype(np.uint8) red=st.fast_bin_average2(red0,cam.weights); red=st.fill_by_mean2(red,7, mask=(np.isnan(red)) & cam.valid) red[invalid]=np.nan; red -= st.rolling_mean2(red,int(self.nx//6.666)) red[red>50]=50; red[red<-50]=-50 red=(red+50)*2.54+1; red[invalid]=0; self.red=red.astype(np.uint8) if rgb: im=np.zeros((self.ny,self.nx,3),dtype=im0.dtype) for i in range(3): im[:,:,i]=st.fast_bin_average2(im0[:,:,i],cam.weights); im[:,:,i]=st.fill_by_mean2(im[:,:,i],7, ignore=0, mask=(im[:,:,i]==0) & (cam.valid)) # im[:,:,i]=st.fill_by_mean2(im[:,:,i],7, ignore=0, mask=np.isnan(red)) im[self.red<=0]=0 self.rgb=im