예제 #1
0
    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
예제 #3
0
    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
예제 #4
0
    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;
예제 #6
0
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")

예제 #7
0
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   



    
예제 #8
0
    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