Beispiel #1
0
def pipeline(img, lines=None, move_lines=False):
    '''
    pipeline for simulating the controller and interlock
    :param img: The image to be processed
    :param lines: proposed lane lines; if not given, a default lane-finding algorithm will generate proposed lines
    :param move_lines: whether or not to alter the proposed lane lines
    :return: whether or not the final proposed lane lines pass
    '''
    # RGB_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img, source_pts, dest_pts = crop(img, SOURCE_PTS, DEST_PTS,
                                     img.shape[0] // 2, 150)
    birds_eye = BirdsEye(source_pts, dest_pts)
    if not lines:
        lines = get_lines(img, birds_eye, REG_THRESHOLDS)
    if move_lines:
        offset(lines, True, 1)

    img, lines, source_pts, dest_pts = resize(img, lines, source_pts, dest_pts,
                                              SCALE_FACTOR)

    interlock_birdseye = BirdsEye(source_pts, dest_pts)
    print("Size is: ", get_size(img))
    print(get_size(interlock_birdseye))
    print(get_size(REG_THRESHOLDS))
    print(get_size(lines))
    shape_result, left_result, right_result = interlock(
        img, interlock_birdseye, REG_THRESHOLDS, lines)
    return shape_result and left_result and right_result
Beispiel #2
0
    def frontogenesis(self,
                      time,
                      level,
                      wrf_sd=0,
                      out_sd=0,
                      dom=1,
                      clvs=0,
                      no_title=1):
        """
        Compute and plot (Miller?) frontogenesis as d/dt of theta gradient.
        
        Use a centred-in-time derivative; hence, if
        time index is start or end of wrfout file, skip the plot.
        """

        outpath = self.get_outpath(out_sd)
        self.W = self.get_wrfout(wrf_sd, dom=dom)
        tstr = utils.string_from_time('output', time)

        Front = self.W.compute_frontogenesis(time, level)

        if isinstance(Front, N.ndarray):
            F = BirdsEye(self.C, self.W)
            fname = 'frontogen_{0}.png'.format(tstr)
            F.plot_data(Front,
                        'contourf',
                        outpath,
                        fname,
                        time,
                        clvs=clvs,
                        no_title=no_title)
        else:
            print("Skipping this time; at start or end of run.")
Beispiel #3
0
    def plot_strongest_wind(self,
                            itime,
                            ftime,
                            levels,
                            wrf_sd=0,
                            wrf_nc=0,
                            out_sd=0,
                            f_prefix=0,
                            f_suffix=0,
                            bounding=0,
                            dom=0):
        """
        Plot strongest wind at level lv between itime and ftime.
        
        Path to wrfout file is in config file.
        Path to plot output is also in config


        Inputs:
        levels      :   level(s) for wind
        wrf_sd      :   string - subdirectory of wrfout file
        wrf_nc      :   filename of wrf file requested.
                            If no wrfout file is explicitly specified, the
                            netCDF file in that folder is chosen if unambiguous.
        out_sd      :   subdirectory of output .png.
        f_prefix    :   custom filename prefix
        f_suffix    :   custom filename suffix
        bounding    :   list of four floats (Nlim, Elim, Slim, Wlim):
            Nlim    :   northern limit
            Elim    :   eastern limit
            Slim    :   southern limit
            Wlim    :   western limit
        smooth      :   smoothing. 0 is off. non-zero integer is the degree
                        of smoothing, to be specified.
        dom         :   domain for plotting. If zero, the only netCDF file present
                        will be plotted. If list of integers, the script will loop over domains.
                        
                        
        """
        self.W = self.get_wrfout(wrf_sd, wrf_nc, dom=dom)

        outpath = self.get_outpath(out_sd)

        # Make sure times are in datenum format and sequence.
        it = utils.ensure_sequence_datenum(itime)
        ft = utils.ensure_sequence_datenum(ftime)

        d_list = utils.get_sequence(dom)
        lv_list = utils.get_sequence(levels)

        for l, d in itertools.product(lv_list, d_list):
            F = BirdsEye(self.C, self.W)
            F.plot2D('strongestwind',
                     it + ft,
                     l,
                     d,
                     outpath,
                     bounding=bounding)
Beispiel #4
0
    def plot_streamlines(self, lv, time, wrf_sd=0, wrf_nc=0, out_sd=0, dom=1):
        self.W = self.get_wrfout(wrf_sd, wrf_nc, dom=dom)
        outpath = self.get_outpath(out_sd)

        self.F = BirdsEye(self.C, self.W)
        disp_t = utils.string_from_time('title', time)
        print("Plotting {0} at lv {1} for time {2}.".format(
            'streamlines', lv, disp_t))
        self.F.plot_streamlines(lv, time, outpath)
Beispiel #5
0
    def std(self, t, lv, va, wrf_sds, out_sd, dom=1, clvs=0):
        """Compute standard deviation of all members
        for given variable.
        
        Inputs:
        t       :   time
        lv      :   level
        va      :   variable
        wrf_sds :   list of wrf subdirs to loop over

        Optional
        out_sd  :   directory in which to save image
        clvs    :   user-set contour levels
        """

        outpath = self.get_outpath(out_sd)

        ncfiles = self.list_ncfiles(wrf_sds)

        # Use first wrfout to initialise grid, get indices
        self.W = self.get_wrfout(wrf_sds[0], dom=dom)

        tidx = self.W.get_time_idx(t)

        if lv == 2000:
            # lvidx = None
            lvidx = 0
        else:
            print("Only support surface right now")
            raise Exception

        std_data = stats.std(ncfiles, va, tidx, lvidx)

        F = BirdsEye(self.C, self.W)
        t_name = utils.string_from_time('output', t)
        fname_t = 'std_{0}_{1}'.format(va, t_name)

        # pdb.set_trace()
        plotkwargs = {}
        plotkwargs['no_title'] = 1
        if isinstance(clvs, N.ndarray):
            plotkwargs['clvs'] = clvs
        F.plot_data(std_data, 'contourf', outpath, fname_t, t, **plotkwargs)
        print("Plotting std dev for {0} at time {1}".format(va, t_name))
Beispiel #6
0
    def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0):
        """Plot a longitude--latitude cross-section (bird's-eye-view).
        Use Basemap to create geographical data
        
        ========
        REQUIRED
        ========

        va = variable(s)
        pt = plot time(s)
        nc = ensemble member(s)
        lv = level(s) 
        p2p = path to plots

        ========
        OPTIONAL
        ========

        da = smaller domain area(s), needs dictionary || DEFAULT = 0
        na = naming scheme for plot files || DEFAULT = get what you're given

        """
        va = self.get_sequence(va)
        pt = self.get_sequence(pt,SoS=1)
        en = self.get_sequence(en)
        lv = self.get_sequence(lv)
        da = self.get_sequence(da)

        perms = self.make_iterator(va,pt,en,lv,da)
        
        # Find some way of looping over wrfout files first, avoiding need
        # to create new W instances
        # print("Beginning plotting of {0} figures.".format(len(list(perms))))
        #pdb.set_trace() 

        for x in perms:
            va,pt,en,lv,da = x
            W = WRFOut(en)    # wrfout file class using path
            F = BirdsEye(self.C,W,p2p)    # 2D figure class
            F.plot2D(va,pt,en,lv,da,na)  # Plot/save figure
            pt_s = utils.string_from_time('title',pt)
            print("Plotting from file {0}: \n variable = {1}" 
                  " time = {2}, level = {3}, area = {4}.".format(en,va,pt_s,lv,da))
Beispiel #7
0
    def upperlevel_W(self,
                     time,
                     level,
                     wrf_sd=0,
                     out_sd=0,
                     dom=1,
                     clvs=0,
                     no_title=1):
        # import pdb; pdb.set_trace()
        outpath = self.get_outpath(out_sd)
        self.W = self.get_wrfout(wrf_sd, dom=dom)

        data = self.W.isosurface_p('W', time, level)
        F = BirdsEye(self.C, self.W)
        tstr = utils.string_from_time('output', time)
        fname = 'W_{0}_{1}.png'.format(level, tstr)
        F.plot_data(data,
                    'contourf',
                    outpath,
                    fname,
                    time,
                    clvs=clvs,
                    no_title=no_title)
Beispiel #8
0
    def spaghetti(self, t, lv, va, contour, wrf_sds, out_sd, dom=1):
        """
        Do a multi-member spaghetti plot.

        t       :   time for plot
        va      :   variable in question
        contour :   value to contour for each member
        wrf_sds :   list of wrf subdirs to loop over
        out_sd  :   directory to save image
        """
        # import pdb; pdb.set_trace()
        outpath = self.get_outpath(out_sd)

        # Use first wrfout to initialise grid etc
        self.W = self.get_wrfout(wrf_sds[0], dom=dom)
        F = BirdsEye(self.C, self.W)

        ncfiles = []
        for wrf_sd in wrf_sds:
            ncfile = self.get_wrfout(wrf_sd, dom=dom, path_only=1)
            ncfiles.append(ncfile)

        F.spaghetti(t, lv, va, contour, ncfiles, outpath)
Beispiel #9
0
 def draw_transect(self, outpath, fname):
     B = BirdsEye(self.C, self.W)
     m, x, y = B.basemap_setup()
     m.drawgreatcircle(self.lonA, self.latA, self.lonB, self.latB)
     self.save(B.fig, outpath, fname)
def pipeline_test(path):
  img = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
  birdseye_img, sobel_img, color_img, curve_debug_img, projected_img, left_radius, right_radius = pipeline_debug(img)
  print("left radius:", left_radius, "m |", "right radius:", right_radius, "m")
  # print(words)
  show_images([birdseye_img, sobel_img, color_img], per_row = 3, per_col = 1, W = 15, H = 3)
  show_images([curve_debug_img, projected_img], per_row = 3, per_col = 1, W = 15, H = 3)

if __name__ == "__main__":

	# calibration = cc.Calibration('./camera_cal', 'jpg', 9, 6)
	# calibration.cameraCalibration(_drawCorner = False)
	# calibration.undistort_list(calibration.getDataList())

	birdsEye = BirdsEye(src_p, dst_p)
	laneFilter = LaneFilter(p)
	curves = Curves(number_of_windows = 9, margin = 100, minimum_pixels = 50, 
                ym_per_pix = 30 / 720 , xm_per_pix = 3.7 / 700)
	
	for i, filename in enumerate(test_img_list):
		img = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
		# warped = birdsEye.sky_view(img)
		
		# blur_ksize = 5  # Gaussian blur kernel size
		# blur_gray = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0, 0)
		# canny_lthreshold = 50  # Canny edge detection low threshold
		# canny_hthreshold = 150  # Canny edge detection high threshold
		# edges = cv2.Canny(blur_gray, canny_lthreshold, canny_hthreshold)

		# lane_filter_test(filename, laneFilter)
Beispiel #11
0
import builtins
import os
if os.getenv('TIMEFRED_BIRDSEYE'):
    
    from birdseye import BirdsEye
    
    eye = BirdsEye(num_samples=dict(
            big=dict(
                    attributes=1000,
                    dict=1000,
                    list=1000,
                    set=1000,
                    pandas_rows=20,
                    pandas_cols=100,
                    ),
            small=dict(
                    attributes=1000,
                    dict=1000,
                    list=1000,
                    set=1000,
                    pandas_rows=6,
                    pandas_cols=10,
                    ),
            )
            )
    import cheap_repr
    
    
    cheap_repr.max_cols = 1000
    cheap_repr.max_level = 1000
    cheap_repr.max_rows = 1000
Beispiel #12
0
#source_points = [(360, 450), (50, 700), (1200, 700), (950, 450)]  # for 1280x720
#destination_points = [(320, 0), (320, 720), (960, 720), (960, 0)] # for 1280x720
source_points = [(180, 180), (0, 360), (640, 360), (460, 180)]  # for 640x360
destination_points = [(160, 0), (160, 360), (480, 360),
                      (480, 0)]  # for 640x360

p = {
    'sat_thresh': 120,
    'light_thresh': 40,
    'light_thresh_agr': 205,
    'grad_thresh': (0.7, 1.4),
    'mag_thresh': 40,
    'x_thresh': 20
}

birdsEye = BirdsEye(source_points, destination_points, matrix, distortion_coef)
laneFilter = LaneFilter(p)
curves = Curves(number_of_windows=1,
                margin=100,
                minimum_pixels=50,
                ym_per_pix=30.0 / 720,
                xm_per_pix=3.7 / 700)

bridge = CvBridge()

# ROS Publisher
pub_image = rospy.Publisher('/lane_image', Image, queue_size=1)
pub_values = rospy.Publisher('/lane_values', String, queue_size=1)
pub_sky_view = rospy.Publisher('/sky_view', Image, queue_size=1)

import time
Beispiel #13
0
    def twopanel_profile(self,
                         va,
                         time,
                         wrf_sds,
                         out_sd,
                         two_panel=1,
                         dom=1,
                         mean=1,
                         std=1,
                         xlim=0,
                         ylim=0,
                         latlon=0,
                         locname=0,
                         overlay=0,
                         ml=-2):
        """
        Create two-panel figure with profile location on map,
        with profile of all ensemble members in comparison.

        Inputs:
        va          :   variable for profile
        time        :   time of plot
        wrf_sds     :   subdirs containing wrf file
        out_d       :   out directory for plots

        Optional:
        two_panel   :   add inset for plot location
        dom         :   WRF domain to use
        mean        :   overlay mean on profile
        std         :   overlay +/- std dev on profile
        xlim        :   three-item list/tuple with limits, spacing interval
                        for xaxis, in whatever default units
        ylim        :   similarly for yaxis but in hPa
                        or dictionary with locations (METAR etc) and two-item tuple
        latlon      :   two-item list/tuple with lat/lon.
                        If not specified, use pop-ups to select.
        locname     :   pass this to the filename of output for saving
        overlay     :   data from the same time to overlay on inset
        ml          :   member level. negative number that corresponds to the 
                        folder in absolute string for naming purposes.


        """
        # Initialise with first wrfout file
        self.W = self.get_wrfout(wrf_sds[0], dom=dom)
        outpath = self.get_outpath(out_sd)

        # Get list of all wrfout files
        enspaths = self.list_ncfiles(wrf_sds)

        self.data = 0
        if two_panel:
            P2 = Figure(self.C, self.W, layout='inseth')
            if overlay:
                F = BirdsEye(self.C, self.W)
                self.data = F.plot2D('cref',
                                     time,
                                     2000,
                                     dom,
                                     outpath,
                                     save=0,
                                     return_data=1)

        # Create basemap for clicker object
        # F = BirdsEye(self.C,self.W)
        # self.data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1)

        # TODO: Not sure basemap inset works for lat/lon specified
        if isinstance(latlon, collections.Sequence):
            if not len(latlon) == 2:
                print(
                    "Latitude and longitude needs to be two-item list/tuple.")
                raise Exception
            lat0, lon0 = latlon
            C = Clicker(self.C, self.W, fig=P2.fig, ax=P2.ax0, data=self.data)
            x0, y0 = C.bmap(lon0, lat0)
            C.ax.scatter(x0, y0, marker='x')
        else:
            t_long = utils.string_from_time('output', time)
            print("Pick location for {0}".format(t_long))
            C = Clicker(self.C, self.W, fig=P2.fig, ax=P2.ax0, data=self.data)
            # fig should be P2.fig.
            # C.fig.tight_layout()

            # Pick location for profile
            C.click_x_y(plotpoint=1)
            lon0, lat0 = C.bmap(C.x0, C.y0, inverse=True)

        # Compute profile
        P = Profile(self.C)
        P.composite_profile(va,
                            time, (lat0, lon0),
                            enspaths,
                            outpath,
                            dom=dom,
                            mean=mean,
                            std=std,
                            xlim=xlim,
                            ylim=ylim,
                            fig=P2.fig,
                            ax=P2.ax1,
                            locname=locname,
                            ml=ml)
Beispiel #14
0
    def plot2D(self,
               vrbl,
               times,
               levels,
               wrf_sd=0,
               wrf_nc=0,
               out_sd=0,
               f_prefix=0,
               f_suffix=0,
               bounding=0,
               dom=0):
        """
        Path to wrfout file is in config file.
        Path to plot output is also in config

        This script is top-most and decides if the variables is
        built into WRF default output or needs computing. It unstaggers
        and slices data from the wrfout file appropriately.


        Inputs:
        vrbl        :   string of variable name
        times       :   one or more date/times.
                        Can be tuple format (YYYY,MM,DD,HH,MM,SS - calendar.timegm)
                        Can be integer of datenum. (time.gmtime)
                        Can be a tuple or list of either.
        levels      :   one or more levels.
                        Lowest model level is integer 2000.
                        Pressure level is integer in hPa, e.g. 850
                        Isentropic surface is a string + K, e.g. '320K'
                        Geometric height is a string + m, e.g. '4000m'
        wrf_sd      :   string - subdirectory of wrfout file
        wrf_nc      :   filename of wrf file requested.
                            If no wrfout file is explicitly specified, the
                            netCDF file in that folder is chosen if unambiguous.
        out_sd      :   subdirectory of output .png.
        f_prefix    :   custom filename prefix
        f_suffix    :   custom filename suffix
        bounding    :   list of four floats (Nlim, Elim, Slim, Wlim):
            Nlim    :   northern limit
            Elim    :   eastern limit
            Slim    :   southern limit
            Wlim    :   western limit
        smooth      :   smoothing. 0 is off. non-zero integer is the degree
                        of smoothing, to be specified.
        dom         :   domain for plotting. If zero, the only netCDF file present
                        will be plotted. If list of integers, the script will loop over domains.
                        
                        
        """
        # import pdb; pdb.set_trace()
        self.W = self.get_wrfout(wrf_sd, wrf_nc, dom=dom)

        outpath = self.get_outpath(out_sd)

        # Make sure times are in datenum format and sequence.
        t_list = utils.ensure_sequence_datenum(times)

        d_list = utils.get_sequence(dom)
        lv_list = utils.get_sequence(levels)
        for t, l, d in itertools.product(t_list, lv_list, d_list):
            F = BirdsEye(self.C, self.W)
            F.plot2D(vrbl, t, l, d, outpath, bounding=bounding)
Beispiel #15
0
    def plot_diff_energy(self,
                         ptype,
                         energy,
                         time,
                         folder,
                         fname,
                         p2p,
                         plotname,
                         V,
                         no_title=0,
                         ax=0):
        """
        
        folder  :   directory holding computed data
        fname   :   naming scheme of required files
        p2p     :   root directory for plots
        V       :   constant values to contour at
        """
        sw = 0

        DATA = self.load_data(folder, fname, format='pickle')

        if isinstance(time, collections.Sequence):
            time = calendar.timegm(time)

        #for n,t in enumerate(times):

        for pn, perm in enumerate(DATA):
            f1 = DATA[perm]['file1']
            f2 = DATA[perm]['file2']
            if sw == 0:
                # Get times and info about nc files
                # First time to save power
                W1 = WRFOut(f1)
                permtimes = DATA[perm]['times']
                sw = 1

            # Find array for required time
            x = N.where(N.array(permtimes) == time)[0][0]
            data = DATA[perm]['values'][x][0]
            if not pn:
                stack = data
            else:
                stack = N.dstack((data, stack))
                stack_average = N.average(stack, axis=2)

        if ax:
            kwargs1 = {'ax': ax}
            kwargs2 = {'save': 0}
        #birdseye plot with basemap of DKE/DTE
        F = BirdsEye(self.C, W1, **kwargs1)  # 2D figure class
        #F.plot2D(va,t,en,lv,da,na)  # Plot/save figure
        tstr = utils.string_from_time('output', time)
        fname_t = ''.join((plotname, '_{0}'.format(tstr)))
        # fpath = os.path.join(p2p,fname_t)
        fig_obj = F.plot_data(stack_average,
                              'contourf',
                              p2p,
                              fname_t,
                              time,
                              V,
                              no_title=no_title,
                              **kwargs2)

        if ax:
            return fig_obj
Beispiel #16
0
    def cold_pool_strength(self,
                           time,
                           wrf_sd=0,
                           wrf_nc=0,
                           out_sd=0,
                           swath_width=100,
                           dom=1,
                           twoplot=0,
                           fig=0,
                           axes=0,
                           dz=0):
        """
        Pick A, B points on sim ref overlay
        This sets the angle between north and line AB
        Also sets the length in along-line direction
        For every gridpt along line AB:
            Locate gust front via shear
            Starting at front, do 3-grid-pt-average in line-normal
            direction
            
        time    :   time (tuple or datenum) to plot
        wrf_sd  :   string - subdirectory of wrfout file
        wrf_nc  :   filename of wrf file requested.
                            If no wrfout file is explicitly specified, the
                            netCDF file in that folder is chosen if unambiguous.
        out_sd      :   subdirectory of output .png.
        swath_width :   length in gridpoints in cross-section-normal direction
        dom     :   domain number
        return2 :   return two figures. cold pool strength and cref/cross-section.
        axes    :   if two-length tuple, this is the first and second axes for
                    cross-section/cref and cold pool strength, respectively
        dz      :   plot height of cold pool only.
        
        """
        # Initialise
        self.W = self.get_wrfout(wrf_sd, wrf_nc, dom=dom)
        outpath = self.get_outpath(out_sd)

        # keyword arguments for plots
        line_kwargs = {}
        cps_kwargs = {}
        # Create two-panel figure
        if twoplot:
            P2 = Figure(self.C, self.W, plotn=(1, 2))
            line_kwargs['ax'] = P2.ax.flat[0]
            line_kwargs['fig'] = P2.fig
            P2.ax.flat[0].set_size_inches(3, 3)

            cps_kwargs['ax'] = P2.ax.flat[1]
            cps_kwargs['fig'] = P2.fig
            P2.ax.flat[1].set_size_inches(6, 6)

        elif isinstance(axes, tuple) and len(axes) == 2:
            line_kwargs['ax'] = axes[0]
            line_kwargs['fig'] = fig

            cps_kwargs['ax'] = axes[1]
            cps_kwargs['fig'] = fig

            return_ax = 1

        # Plot sim ref, send basemap axis to clicker function
        F = BirdsEye(self.C, self.W)
        self.data = F.plot2D('cref',
                             time,
                             2000,
                             dom,
                             outpath,
                             save=0,
                             return_data=1)

        C = Clicker(self.C, self.W, data=self.data, **line_kwargs)
        # C.fig.tight_layout()

        # Line from front to back of system
        C.draw_line()
        # C.draw_box()
        lon0, lat0 = C.bmap(C.x0, C.y0, inverse=True)
        lon1, lat1 = C.bmap(C.x1, C.y1, inverse=True)

        # Pick location for environmental dpt
        # C.click_x_y()
        # Here, it is the end of the cross-section
        lon_env, lat_env = C.bmap(C.x1, C.y1, inverse=True)
        y_env, x_env, exactlat, exactlon = utils.getXY(self.W.lats1D,
                                                       self.W.lons1D, lat_env,
                                                       lon_env)
        # Create the cross-section object
        X = CrossSection(self.C, self.W, lat0, lon0, lat1, lon1)

        # Ask user the line-normal box width (self.km)
        #C.set_box_width(X)

        # Compute the grid (DX x DY)
        cps = self.W.cold_pool_strength(X,
                                        time,
                                        swath_width=swath_width,
                                        env=(x_env, y_env),
                                        dz=dz)
        # import pdb; pdb.set_trace()

        # Plot this array
        CPfig = BirdsEye(self.C, self.W, **cps_kwargs)
        tstr = utils.string_from_time('output', time)
        if dz:
            fprefix = 'ColdPoolDepth_'
        else:
            fprefix = 'ColdPoolStrength_'
        fname = fprefix + tstr

        pdb.set_trace()
        # imfig,imax = plt.subplots(1)
        # imax.imshow(cps)
        # plt.show(imfig)
        # CPfig.plot_data(cps,'contourf',outpath,fname,time,V=N.arange(5,105,5))
        mplcommand = 'contour'
        plotkwargs = {}
        if dz:
            clvs = N.arange(100, 5100, 100)
        else:
            clvs = N.arange(10, 85, 2.5)
        if mplcommand[:7] == 'contour':
            plotkwargs['levels'] = clvs
            plotkwargs['cmap'] = plt.cm.ocean_r
        cf2 = CPfig.plot_data(cps, mplcommand, outpath, fname, time,
                              **plotkwargs)
        # CPfig.fig.tight_layout()

        plt.close(fig)

        if twoplot:
            P2.save(outpath, fname + "_twopanel")

        if return_ax:
            return C.cf, cf2