Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - horshack

#26
Modules Development / Re: DotTune AFMA
March 04, 2013, 04:03:59 AM
Quote from: Francis on March 03, 2013, 06:15:42 PM
I have a 100mm macro lens that I use both for macro shots and for portraits. Running DotTune at both a macro and a portrait distance gives different results (-5 at portrait, +4 at macro). Sound I use the average? 0 and -1 lies in the acceptable range for both. Does that sound reasonable or am I missing something? The author suggests 50 x focal length. I almost never use this lens at 5m so it doesn't seem appropriate to set the MA at that distance.

It's not unsual for lenses to exhibit focus-distance variance in tune values. You can either choose the value for a single distance if that's what you shoot at most often, or use the midpoint of the distance values if that produces acceptable results for you.
#27
Modules Development / Re: DotTune AFMA
March 04, 2013, 04:00:53 AM
Quote from: a1ex on March 03, 2013, 01:19:14 PM
Just did a slightly bigger change: besides adding support for wide/tele adjustments, I've moved all AFMA code to afma.c and kept only the constants in platform directory (afma.h).

So... it needs some testing to make sure I didn't break anything.

Thanks Alex. Works fine on my 5DM3. The only new issue I found is if the user cancels or DotTune ends in error, the final call to set_afma(afma0, afma_mode) will fail to properly restore the original tune value when afma_mode is AFMA_MODE_PER_LENS, since get_afma(AFMA_MODE_PER_LENS) returns the midpoint of the wide/tele settings for #CONFIG_AFMA_WIDE_TELE builds:

return (AFMA_PER_LENS_WIDE + AFMA_PER_LENS_TELE) / 2;

The logic in afma_auto_tune() will need to save off and restore both wide/tele values for the user cancelled/DotTune failed case.

I'm also thinking we might want to consider removing the "both" mode for #CONFIG_AFMA_WIDE_TELE builds and instead force the user to always set wide or tele. This is based on my reading on the 5DM3 manual - when only a single value is configured (wide or tele), the camera will still scale that tune value for other focal lengths on the zoom, and so having the wide+tele set to the same tune value might not be ideal for the majority of users, even if it would be useful for a small percentage of them.
#28
Modules Development / Re: DotTune AFMA
March 02, 2013, 08:59:25 PM
Alex,

I added logic for the 5DM3's unique wide/tele AFMA support. The source is below. When DotTuning a zoom and specifying a wide vs tele value (instead of "both"), the user needs to make sure he sets his lens focal length to match where he'll be storing his wide/tele tune value before starting the DotTune, because otherwise the 5DM3's firmware will interpolate AF tune values between the wide and tele settings and this is not desirable when tunning. For example, when focusing (or evaluating focus via the VF confirmation), if AFMA is -5 for wide and -10 for tele on a 70-200mm lens, @ 70mm the 5DM3 firmware will use -5, @ 200mm it will use -10, and for any focal length in between it will use a scaled value between -5 and -10, per Canon's 5DM3 manual and my observations as well.

Here's the source. Again I'm new to ML so this might require some modification as you see fit. Thanks!

src/dryos.h:
/** AF microadjustment **/
int get_afma(int mode, int focalVal);
void set_afma(int value, int mode, int focalVal);
#define AFMA_MODE_AUTODETECT -1
#define AFMA_MODE_DISABLED 0
#define AFMA_MODE_ALL_LENSES 1
#define AFMA_MODE_PER_LENS 2
#define AFMA_FOCAL_VAL_BOTH 0
#define AFMA_FOCAL_VAL_WIDE 1
#define AFMA_FOCAL_VAL_TELE 2


platform/5D3 cfn.c:
// 5D3 only; other cameras have different offsets, buffer size etc
#define PROP_AFMA_CFN 0x80040027
static int8_t afma_buf[0x10];
#define AFMA_MODE          afma_buf[0x0]
#define AFMA_PER_LENS_WIDE afma_buf[0x2]
#define AFMA_PER_LENS_TELE afma_buf[0x3]
#define AFMA_ALL_LENSES    afma_buf[0x5]

int get_afma(int mode, int focalVal)
{
    if (mode == AFMA_MODE_AUTODETECT) mode = AFMA_MODE;
   
    if (mode == AFMA_MODE_PER_LENS)
        // for AFMA_FOCAL_VAL_BOTH, return AFMA_PER_LENS_WIDE
        return (focalVal <= AFMA_FOCAL_VAL_WIDE ? AFMA_PER_LENS_WIDE : AFMA_PER_LENS_TELE);

    else if (mode == AFMA_MODE_ALL_LENSES)
        return AFMA_ALL_LENSES;

    return 0;
}

void set_afma(int value, int mode, int focalVal)
{
    if (ml_shutdown_requested) return;

    value = COERCE(value, -AFMA_MAX, AFMA_MAX);

    if (mode == AFMA_MODE_AUTODETECT) mode = AFMA_MODE;
    if (mode == AFMA_MODE_DISABLED) mode = AFMA_MODE_ALL_LENSES;
   
    if (mode == AFMA_MODE_PER_LENS) {
        if (focalVal == AFMA_FOCAL_VAL_BOTH)
            AFMA_PER_LENS_WIDE = AFMA_PER_LENS_TELE = value;
        else if (focalVal == AFMA_FOCAL_VAL_WIDE)
            AFMA_PER_LENS_WIDE =  value;
        else if (focalVal == AFMA_FOCAL_VAL_TELE)
            AFMA_PER_LENS_TELE = value;
        else return; // bad arguments
    }
   
    else if (mode == AFMA_MODE_ALL_LENSES)
        AFMA_ALL_LENSES = value;
   
    else return; // bad arguments
   
    AFMA_MODE = mode;
    prop_request_change(PROP_AFMA_CFN, afma_buf, sizeof(afma_buf));
}


platform/5D2 cfn.c:
int get_afma(int mode, int focalVal /* not supported on 5D2 */)
{
    if (mode == AFMA_MODE_AUTODETECT) mode = AFMA_MODE;
   
    if (mode == AFMA_MODE_PER_LENS)
        return AFMA_PER_LENS;

    else if (mode == AFMA_MODE_ALL_LENSES)
        return AFMA_ALL_LENSES;

    return 0;
}

void set_afma(int value, int mode, int focalVal /* not supported on 5D2 */)
{
    if (ml_shutdown_requested) return;

    value = COERCE(value, -AFMA_MAX, AFMA_MAX);

    if (mode == AFMA_MODE_AUTODETECT) mode = AFMA_MODE;
    if (mode == AFMA_MODE_DISABLED) mode = AFMA_MODE_ALL_LENSES;

    if (mode == AFMA_MODE_PER_LENS)
        AFMA_PER_LENS = value;
   
    else if (mode == AFMA_MODE_ALL_LENSES)
        AFMA_ALL_LENSES = value;
   
    else return; // bad arguments

    AFMA_MODE = mode;
    prop_request_change(PROP_AFMA_CFN, afma_buf, sizeof(afma_buf));
}


src/focus.c:
#ifdef FEATURE_AFMA_TUNING

CONFIG_INT( "focal_afma_value_select", focal_afma_value_select, AFMA_FOCAL_VAL_BOTH);

static void afma_print_status(int8_t* score, int range_expand_factor)
{

Changed all get_afma() and set_afma() calls in focus.c to pass "focal_afma_value_select" as final argument, which includes afma_print_status(), afma_auto_tune(), MENU_UPDATE_FUNC(afma_display), and MENU_SELECT_FUNC(afma_toggle)

            {
                .name = "AF microadjust",
                .update = afma_display,
                .select = afma_toggle,
                #ifdef CONFIG_AFMA_EXTENDED
                .help  = "Adjust AFMA value manually. Range: -100...+100.",
                #else
                .help  = "Adjust AFMA value manually. Range: -20...+20.",
                #endif
                .edit_mode = EM_MANY_VALUES,
            },
#if defined(CONFIG_5D3)
            {
                .name = "Wide/Tele Select",
                .priv = &focal_afma_value_select,
                .max = 2,
                .choices = (const char *[]) {"Both/Prime", "Wide(Zoom)", "Tele(Zoom)"},
                .help = "Body supports two AFMA values for zoom lenses.",
            },
#endif
            MENU_EOL
        },
    },
};



#29
Modules Development / Re: DotTune AFMA
February 26, 2013, 10:06:46 AM
Alex,

That change looks good. I haven't tested the expanded ranges yet; if for whatever reason those don't work out we might want to replace the user suggestion of trying a wider range with a warning message that the new tune value may not be reliable since -20 or +20 were part of the range.

Today I spent about an hour on the core timing of the focus confirmation detection, comparing it to my own visual AF confirmation checks. Rather than just verifying the final calculated value like I was before, today I was comparing the behavior of individual tune values at the periphery of the tuning range. Your algo vs my visual check matches well; your idea to do 4 sweeps is perfect because I do see a blip on one or two passes so the 4 passes is just right for normalizing them out.

The only worthwhile improvement I found was to change to 500ms dead delay into a continuous FOCUS_CONFIRMATION check; right now the code is waiting 500ms and then sampling in a separate loop, which misses some multiple-beep/confirmation cases I see at the outliers on my 50mm f/1.4 (ie, tune values that should be discarded but are included). So rather than sampling for one positive confirmation after 500ms it catches more blips if we sample continuously.

Here's the proposed change. Since this change involves another sampling loop I consolidated that into a separate nested function. Let me know what you think...

    SW1(1,100);

    int wait_for_focus_confirmation_val(int maxTimeToWaitMs, int valToWaitFor)
    {
        int timeWaitedMs = 0;
        for ( ;; )
        {
            if (FOCUS_CONFIRMATION == valToWaitFor)
                return 1; // detected value
            if (timeWaitedMs >= maxTimeToWaitMs)
                return 0; // timed-out before detecting value
            msleep(10);
            timeWaitedMs += 10;
        }
    }

    for (int k = 0; k < 2; k++)
    {
        for (int c = 0; c <= 80; c++) // -20 ... 20 and back to -20
        {
            int i = (c <= 40) ? c-20 : 60-c;
            set_afma(i * range_expand_factor, AFMA_MODE_AUTODETECT);
            msleep(100);
           
            int fc;
            // initial focus must occur within 200ms
            fc = wait_for_focus_confirmation_val(200, 1);
           
            if (fc)
            {
                // weak or strong confirmation? use a higher score if strong
                // focus must sustain for 500ms to be considered strong
                if (!wait_for_focus_confirmation_val(500, 0))
                    // focus sustained
                    fc = 3;
            }
           
            score[i+20] += fc;
            afma_print_status(score, range_expand_factor);
           
            if (!HALFSHUTTER_PRESSED)
            {
                beep();
                set_afma(afma0, AFMA_MODE_AUTODETECT);
                NotifyBox(2000, "Canceled by user.");
                return;
            }
        }
    }
    SW1(0,100);

Thanks,

Horshack
#30
Modules Development / Re: DotTune AFMA
February 25, 2013, 05:51:18 AM
Alex,

I've been tied up this weekend but I'm getting back to this for more testing. Q about the current algo - I noticed your two focus loops continue to execute even after detecting FOCUS_CONFIRMATION. Is this done intentionally for downstream timing, because I can't see any other side effect otherwise. If I insert a break after FOCUS_CONFIRMATION I get the same tune results and shave about 30 seconds off the DotTune time, dropping from 1:47 to 1:18 for a range that has about 20 tune units.

Here's the modified source (change in red):

// check for focus confirmation
            int fc = 0;
            for (int j = 0; j < 20; j++)
            {
                msleep(10);
                if (FOCUS_CONFIRMATION) {
                    fc = 1;
                    break;
                }
            }
           
            // weak or strong confirmation? use a higher score if strong
            if (fc)
            {
                msleep(500);
                for (int j = 0; j < 20; j++)
                {
                    msleep(10);
                    if (FOCUS_CONFIRMATION) {
                        fc = 3;
                        break;
                    }
                }
            }

Thanks,

Horshack
#31
Modules Development / Re: DotTune AFMA
February 24, 2013, 05:55:49 PM
Quote from: Marsu42 on February 24, 2013, 11:55:02 AM
Exactly - Canon removed it from the 50d->60d to make the 7d look better (thanks, Canon!). But probably all they did was commenting it out in the ui, so it would be a godsend it you'd figure out if the 60d internally still supports it...

I'm thinking the same. Probably the easiest way to find out for sure is to enable DotTune for the 60D build and see if it finds any tune value between +/- 20 that doesn't produce focus confirmation, as this would imply the 60D's PDAF is using those values. This test assumes ML is using the correct NVRAM property location for the 60D's AF tune values.
#32
Modules Development / Re: DotTune AFMA
February 24, 2013, 05:54:14 AM
Was going to ask if you guys have unlocked the 60D's AFMA support but I just found the wiki. I don't have a 60D but I was wondering if the PDAF system internally supports AFMA.
#33
Modules Development / Re: DotTune AFMA
February 23, 2013, 10:53:59 PM
Quote from: a1ex on February 23, 2013, 10:38:41 PM
Looks very nice, thanks a lot!

I would also like to include a screen which shows perfect focus on the test target (maybe the LiveView 10x screen). Of course, this is up to you.

Thank YOU Alex! Having an in-camera automated AF tune is a remarkable achievement IMO. I suspect once this is becomes available many fresh users will give ML a serious look. Regarding prefocusing in LV, I make mention of it in the voice over, if that's ok for this initial video. When you guys release this officially I promise to do another video with better production value and more exposition, including the LV step.
#34
Modules Development / Re: DotTune AFMA
February 23, 2013, 10:18:55 PM
I've made a video to give others an early peak at this. The video is private right now - wanted to run it by you ML guys to make sure it's ok to make public.

#35
Modules Development / Re: DotTune AFMA
February 23, 2013, 07:54:19 PM
Quote from: a1ex on February 23, 2013, 07:36:38 PM
If the screen normally turns off when you press half-shutter, like 600D... not sure what to do. Maybe call display_on in the print routine?

With 5D3, I have +20 on 50/1.8 at ~1 meter and +8 at ~3 meters. Both figures are quite repeatable.

I just tried your latest. That GUI you put on it is so sweet. Here are the results with my Sigma 35mm f/1.4 @ 1.5 meters so far:

My algorithm: +6, +6, +5, +5
Your algorithm: +6, +5, +5, +5

So they look to be equal. I'll try a few more lenses - if they're all about the same then it looks like your algorithm is good to go! This is so cool.

Edit: Here are my 50mm EF f/1.4 results:

My algorithm: +3, +2, +2, +3
Your algorithm: +1, +2, +2, +2
#36
Modules Development / Re: DotTune AFMA
February 23, 2013, 05:55:37 PM
Quote from: a1ex on February 23, 2013, 01:43:44 PM
Very nice.

I've just added the extended AFMA ranges and they seem to work both on 5D2 and 5D3. They are interpreted properly in PDAF, you can try by defocusing a bit before running the calibration.



Now I'd like to compare both algorithms and see if there are any differences.

The extended range support you added is going to be a godsend, not just for when the final AF tune value is beyond +/-20 but also when the DotTune range is beyond +/- 20 but where the midpoint still falls within +/- 20.

How should we go about comparing the algorithms? I have several lenses I could check yours on, but I'm thinking maybe the sample size would be too small. Should we consider leaving both algorithms in and make it user selectable?
#37
Modules Development / Re: DotTune AFMA
February 23, 2013, 12:54:35 PM
Here is my first DotTune under ML! Early indications are it's producing midpoints very close to the manual procedure. More testing is needed though. Also needs a nice UI if anyone is around who knows ML and is interested  :)



Here's the source:

#ifdef FEATURE_AFMA_TUNING
//
// Dot-Tune constants
//
#define AF_TUNE_CHANGE_SETTLE_TIME_MS        (10)   // time for PDAF to resample phase after changing AF tune (swag)
#define MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS   (300)  // max time we allow before 1st focus confirmation (swag)
#define MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS (5000) // min time that focus confirmation must remain TRUE

int check_dottune_focus(void)
{

    int waitTimeMs;

    //
    // detect how long it takes for PD to sample a successful focus
    // confirmation. based on user-level observations, if there is
    // any perceptible delay between half-pressing the shutter and
    // getting the first focus confirmation then the focus confirmation
    // should be disregarded and the AF TUNE value not included in
    // the tuning range.
    //
    waitTimeMs = 0;
    do {
        if (FOCUS_CONFIRMATION)
            break;
        msleep(10);
        waitTimeMs += 10;
    } while (waitTimeMs < MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS);

    if (waitTimeMs >= MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS)
        // no initial focus confirmation within time period allowed
        return 0;

    //
    // detected first focus confirmation within the time period
    // allowed. now make sure the focus confirmation stays
    // continuously on for our threshold before we consider
    // the current tune value part of the confirmed range
    //
    waitTimeMs = 0;
    do {
        if (!FOCUS_CONFIRMATION)
            break;
        msleep(10);
        waitTimeMs += 10;
    } while (waitTimeMs < MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS);

    if (waitTimeMs < MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS)
        // focus dropped during continuous focus check
        return 0;

    //
    // focus remained confirmed for the duration we require, so
    // current AF tune value was confirmed to be part of the
    // confirmed range
    //
    return 1;
}


void afma_auto_tune()
{

    #define TUNE_VALUE_INVALID              (INT_MIN)
    #define MIN_TUNE_VALUE                  (-20)
    #define MAX_TUNE_VALUE                  (20)
 
    int tuneValue, focusConfirmed;
    int confirmedTuneRangeLow, confirmedTuneRangeHigh;

    int afma0 = get_afma(-1);
   
    msleep(1000);
   
    if (lv) { fake_simple_button(BGMT_LV); msleep(1000); }
   
    for (int i = 0; i < 5; i++)
    {
        if (!DISPLAY_IS_ON || !display_idle())
        {
            fake_simple_button(BGMT_INFO);
            msleep(500);
        }
    }

    if (lv) { NotifyBox(5000, "Turn off LiveView and try again."); beep(); return; }
    if (!DISPLAY_IS_ON || !display_idle()) { NotifyBox(5000, "Press " INFO_BTN_NAME " and try again."); beep(); return; }
    if (!is_manual_focus()) { NotifyBox(5000, "Switch to MF and try again."); beep(); return; }
    if (!lens_info.name[0]) { NotifyBox(5000, "Attach a chipped lens and try again."); beep(); return; }

#ifdef DONT_COMPILE_YET // wait until development is done before delaying on these msgs
    NotifyBox(5000, "You should have perfect focus.");
    msleep(2000);
    NotifyBox(5000, "Leave the camera still...");
    msleep(2000);
    NotifyBoxHide();
    msleep(100);
#endif
   
    assign_af_button_to_halfshutter();
    msleep(100);

    //
    // algorithm: with critical focus already established prior to
    // this routine being called, we scan through the full +/- 20
    // AF tune range to find the range of values which produce
    // an immediate and consistent focus confirmation, then use
    // the midpoint of that confirmed range as the optimal AF tune value.
    //
    // when the scan starts we're looking for the first tune value
    // that produces an immediate and consistent focus confirmation;
    // that represents the low end of the confirmed range. once we
    // find that the scan is then looking for the first tune value
    // which fails to produce confirmation; that tune value minus 1
    // represents the high end of the confirmed range.
    //
    confirmedTuneRangeLow = confirmedTuneRangeHigh = TUNE_VALUE_INVALID;

    for (tuneValue=MIN_TUNE_VALUE; tuneValue<=MAX_TUNE_VALUE; tuneValue++) {
       
        NotifyBox(1000, "Checking AF Tune %d", tuneValue);

        //
        // set next AF tune value to test, allow time for PDAF
        // to resample the phase with the new AF TUNE value
        //
        set_afma(tuneValue,-1);
        msleep(AF_TUNE_CHANGE_SETTLE_TIME_MS);

        //
        // check the focus at this AF tune value
        //
        SW1(1,100);
        focusConfirmed = check_dottune_focus();
        SW1(0,100);

        //
        // process focus result
        //
        if (focusConfirmed) { // focus confirmed for current tune value
            if (confirmedTuneRangeLow == TUNE_VALUE_INVALID) {
                // we just found the first end of the range (low end)
                confirmedTuneRangeLow = tuneValue;
                continue; // advance to next tune value to start searching high-end of range
            } else
                //
                // we've previously found the low end of the tune range and
                // are trying to find the first non-confirmed tune
                // value to establish the high-end of the range, so keep scanning
                //
                continue; // advance to next tune value
        } else  { // focus not confirmed for current tune value
            if (confirmedTuneRangeLow != TUNE_VALUE_INVALID) {
                //
                // we've previously found the low end of the tune range and now
                // found our first tune value which is non-confirmed, meaning
                // the high-end of the confirmed range is the current value
                // minus 1
                //
                confirmedTuneRangeHigh = tuneValue-1;
                break; // done finding range; no more tune values to check
            }
        }
    }
   
    restore_af_button_assignment();

    if (confirmedTuneRangeLow != TUNE_VALUE_INVALID && confirmedTuneRangeHigh != TUNE_VALUE_INVALID) {
        //
        // DotTune successful. Optimal tune value is midpoint of range.
        // if the midpoint is between two whole values then we round down
        //
        int midpoint = (confirmedTuneRangeHigh - confirmedTuneRangeLow)/2 + confirmedTuneRangeLow;
        set_afma(midpoint,-1);
        NotifyBox(5000, "Success: Mid=%d (range=%d..%d)",
                midpoint, confirmedTuneRangeLow, confirmedTuneRangeHigh);
        return;
    }

    //
    // DotTune failed
    //
    set_afma(afma0,-1); // restore orig AF tune value
    if (confirmedTuneRangeLow == TUNE_VALUE_INVALID)
        //
        // no AF tune values produced a confirmation, which likely
        // means critical focus was not properly achieved prior
        // to calling this routine
        //
        NotifyBox(5000, "Failed: Image is OOF!");
    else
        //
        // we found the low end of the range but +20 still produced a focus
        // confirmation, so the high end of the range is beyond +20, meaning
        // we can't find the precise midpoint
        //
        NotifyBox(5000, "Failed: +20 exceeded (low=%d)",
                confirmedTuneRangeLow);
}


Horshack
#38
Modules Development / Re: DotTune AFMA
February 23, 2013, 12:44:59 PM
Quote from: a1ex on February 23, 2013, 12:04:08 PM
I've also tried to expand the range to -40...40 and seems to work, the camera is still alive (5D2).

So, the theoretical range would be -127...127. Could this be useful?

If you try on other camera, try in C mode first, to prevent wrong settings being saved in NVRAM.

Absolutely this could be very useful. We'll need to see if the PDAF system itself actually supports these values. If it does this could help a lot of people whose tuning range exceeds +/-20.
#39
Modules Development / Re: DotTune AFMA
February 23, 2013, 10:55:55 AM
Thanks again for the help! Got alpha 3 installed and I'm able to test my own builds. My first task was to compare the programmatic check of the FOCUS_CONFIRMATION variable against the VF feedback and I'm excited to report they're essentially identical. The most critical aspect of DotTune is being able to distinguish confirmed vs non-confirmed values, which on Canon bodies involves very tight timing between the half-press and the first confirmation, along with a continuous confirmation without any dropouts (meaning multiple beeps/dot flickers). I have a particular lens + focus distance setup that causes both these scenarios to negative-confirm at one of the AF tune values and FOCUS_CONFIRMATION matched the VF behavior exactly. This gives me a lot of confidence that the programmatic negative-confirmation methodology will match the user-level DotTune success very well.

I have a few more questions...sorry :)

The 5D3 supports 2 tune values per lens, which is used for zoom lenses so you can tune at each end of the zoom range. It doesn't look like the ML tune property logic that was checked-in is aware of this. I was worried about that so for now I've commented out the programmatic AFMA-changing logic until I can be sure that the logic wont corrupt the NVRAM config on my 5D3. How do you usually go about reverse-engineering the property area, so that we can figure out the data structure for the new 2nd value?

What is the purpose of the "fake_simple_button(BGMT_INFO);" in the AFMA logic you wrote? I put it in my test routine and it cycles through a few info screens before getting to my logic. Is that used so that you have an active on-screen canvas to display messages on? (ie, the NotifyBox calls not work properly without the display being made active)

Regarding simulating a half-press of the shutter, I assume SW1(1,100) asserts a simulated press while SW1(0,100) deasserts it? Is the assign_af_button_to_halfshutter() before those necessary to associate that remote simulate to the shutter?

Thanks again,

Horshack
#40
Modules Development / Re: DotTune AFMA
February 23, 2013, 05:23:06 AM
Quote from: a1ex on February 22, 2013, 10:54:44 PM
The confirmation flag matches the green dot, as far as I could tell.

But even with still camera and high-contrast test pattern, I couldn't get the dot steady for too long. Maybe it was because of artificial light (didn't try in daylight yet).

I'll try to figure it out for 5D3, so you can experiment with the algorithm.

Edit: works on 5D3, you can give it a try.

Thanks again. I got the build environment set up and I'm able to compile the 5D3 autoexec.bin, albeit with a few compiler warnings which I assume are ok? Based on what I've pieced together from google searches, this is what I do:


  • Unzip contents of the 5D3 Alpha 3 release to root directory of an SD card that's been formatted in-camera
  • Copy 5D3-113-bootflag.fir to the root directory
  • Copy my compiled autoexec.bin to the root directory
  • Run EOScard.exe and select EOS_DEVELOP and BOOTDISK boxes
  • Make sure I'm running 1.1.3 of Canon's 5D3 FW
  • Power on camera and perform perform upgrade using the SD card with ML on it

Is that about it? And I just recopy new autoexec.bin and do fresh firmware updates to test each of my compiles? Anything else I should know?
#41
Modules Development / Re: DotTune AFMA
February 22, 2013, 10:45:37 PM
Quote from: a1ex on February 22, 2013, 09:21:17 PM
1. Both. Sampling issue is actually something minor IMO.

2. It needs shutter pressed halfway (SW1).

3. The initial guess seems good.

The idea of waiting for continuous confirmation for 5 seconds is interesting, but in practice I don't think it will work (at least with my setup - 5D2 looking at some very small print). If the confirmation is strong, there are multiple focus beeps, and the dot blinks (but it's mostly on).

Also, camera's AF algorithm is not 100% reliable, so finding the range doesn't quite work. If the AF misses some spot in the middle of the range, the end point will be wrong. So, I prefer the current method, because it's much more statistically robust (even if it uses mean, not median).

I also have a problem when sometimes I get focus confirmations over the entire range. Not sure what it causes this, but changing micro AF in Canon menu seems to solve it. Probably the method I'm using for changing AF value isn't quite right.

Thanks for the replies a1ex, much appreciated. Regarding the robustness of either approach, I think it's going to come down to how the behavior of the internal FOCUS_CONFIRMATION flag correlates to the VF confirmation behavior. If the flag matches the VF behavior, I think the negative-exclusion approach of my code snippet above would be necessary to produce a reliable midpoint/AF-tune value, at least based on feedback from the DotTune beta testers on the original FM thread. In particular, the lag to the initial confirmation and the sustaining of the confirmation without any dropouts were found to be critical for successful tuning, and these aspects might not be captured by a weighted, positive-only sampling of the confirmation, unless perhaps there were a very large number of samples over a long period of time. If however you find the flag behaves differently than the VF confirmation then that would certainly require a different approach to the programmatic implementation vs the user-lever method.

As for the ranging and midpoint method, that's been validated on both Nikon and Canon bodies and feedback indicates it's highly accurate with good precision/repeatability, again at least based on the user-level feedback provided by the VF. There shouldn't be nonlinearities or gaps in the confirmed range.
#42
Modules Development / Re: DotTune AFMA
February 22, 2013, 08:07:58 PM
Quote from: a1ex on February 22, 2013, 11:35:41 AM
Wow, thanks for feedback!

FOCUS_CONFIRMATION is a variable

Thanks a1ex, that gives me a nice overview of the embedded environment.

Algorithmically, the ML implementation would likely want to match the human-level algorithm of DotTune, provided that the behavior FOCUS_CONFIRMATION roughly matches the behavior of confirmation dot delivered in the VF.

Q: Is msleep() only used as a method to measure time and delay, or is it also required to allow background tasks to execute? (non-preemptive yield point for DryOS). If it's the former, do you have an alternate way to measure elapsed time so that the FOCUS_CONFIRMATION loops can sample continuously without yielding? This would eliminate sampling/timing errors of the variable relative to the micro-controller/background task that updates it.

Q: Is FOCUS_CONFIRMATION updated continuously while the camera is powered on or is there an internal trigger needed to initiate the background task sampling, akin to a half-press of the shutter? I see in your source that you call fake_simple_button(). Is that for this purpose?

Q: It might take some trial and error but I think we'd need to establish roughly what the time delta is between changing an AF tune value and when the first PD sensing result in FOCUS_CONFIRMATION reflects that new value. For the purposes of the source provided below I took a swag at 100ms.

Here is source to an implementation that uses an algorithm that matches the user-level steps of DotTune. Some questions remain about how exactly FOCUS_CONFIRMATION works relative to the VF indication, so this logic may need some tweaks after debugging. Unfortunately I sold my 5DM2 some time ago and the only Canon body I have with me is a 5DM3, which seems to still be in alpha for ML.


#define TUNE_VALUE_INVALID              (INT_MIN)
#define MIN_TUNE_VALUE                  (-20)
#define MAX_TUNE_VALUE                  (20)

#define AF_TUNE_CHANGE_SETTLE_TIME_MS        (100)  // time it takes for PDAF to resample phase after changing AF tune (swag)
#define MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS   (300)  // max time we allow before 1st focus confirmation (swag)
#define MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS (5000) // min time that focus confirmation must remain TRUE

int is_focus_confirmed(void) {
    return FOCUS_CONFIRMATION;
}

int perform_dot_tune(void) { // returns optimal AF tune value or TUNE_VALUE_INVALID

    int tuneValue;
    int confirmedTuneRangeLow, confirmedTuneRangeHigh;
    int waitTimeMs;

    //
    // algorithm: with critical focus already established prior to
    // this routine being called, we scan through the full +/- 20
    // AF tune range to find the range of values which produce
    // an immediate and consistent focus confirmation, then use
    // the midpoint of that confirmed range as the optimal AF tune value.
    //
    // when the scan starts we're looking for the first tune value
    // that produces an immediate and consistent focus confirmation;
    // that represents the low end of the confirmed range. once we
    // find that the scan is then looking for the first tune value
    // which fails to produce confirmation; that tune value minus 1
    // represents the high end of the confirmed range.
    //

    confirmedTuneRangeLow = confirmedTuneRangeHigh = TUNE_VALUE_INVALID;

    for (tuneValue=MIN_TUNE_VALUE; tuneValue<=MAX_TUNE_VALUE; tuneValue++) {

        //
        // set next AF tune value, allow time for PDAF to resample the
        // phase with the new AF TUNE value
        //
        set_afma(tuneValue, -1);
        msleep(AF_TUNE_CHANGE_SETTLE_TIME_MS);

        //
        // detect how long it takes for PD to sample a successful focus
        // confirmation. based on user-level observations, if there is
        // any perceptible delay between half-pressing the shutter and
        // getting the first focus confirmation then the focus confirmation
        // should be disregarded and the AF TUNE value not included in
        // the tuning range. since our logic is sampling an internal
        // firmware variable to detect focus confirmation, we would
        // need some way to "turn off" the camera's PD sensing so that
        // we can detect a fresh PD sensing transition akin to a half-press
        // of the shutter. if no such mechanism exists then our logic will
        // likely only be useful for AF tune changes that transition
        // from a non-confirmed to confirmed values (ie, when we're looking
        // for the high end of the range). if so, we might consider
        // changing the algorithm so that we start scanning from the opposite
        // end of the AF tune scale to find the high-end of the range
        //
        waitTimeMs = 0;
        do {
            if (is_focus_confirmed())
                break;
            msleep(10);
            waitTimeMs += 10;
        } while (waitTimeMs < MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS);

        if (waitTimeMs < MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS) {
            //
            // detected first focus confirmation within the time period
            // allowed. now make sure the focus confirmation stays
            // continuously on for our threshold before we consider
            // the current tune value part of the confirmed range
            //
            waitTimeMs = 0;
            do {
                if (!is_focus_confirmed())
                    break;
                msleep(10);
                waitTimeMs += 10;
            } while (waitTimeMs < MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS);

            if (waitTimeMs >= MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS) {
                //
                // focus remained confirmed for the duration we require, so
                // current AF tune value was confirmed to be part of the
                // confirmed range
                //
                if (confirmedTuneRangeLow == TUNE_VALUE_INVALID) {
                    // we just found the first end of the range (low end)
                    confirmedTuneRangeLow = tuneValue;
                    continue; // advance to next tune value to start searching high-end of range
                } else
                    //
                    // we've previously found the low end of the tune range and
                    // are trying to find the first non-confirmed tune
                    // value to establish the high-end of the range
                    //
                    continue; // advance to next tune value
            } // else focus confirmation failed to sustain for minimum time allowed
        } // else first focus confirmation didn't occur within max time allowed

        //
        // we reach this point if the current AF tune value is not confirmed
        //
        if (confirmedTuneRangeLow != TUNE_VALUE_INVALID) {
            //
            // we've previously found the low end of the tune range and now
            // found our first tune value which is non-confirmed, meaning
            // the high-end of the confirmed range is the current value
            // minus 1
            confirmedTuneRangeHigh = tuneValue-1;
            break; // done finding range; no more tune values to check
        }
    } // end of AF tune scan loop

    if (confirmedTuneRangeLow != TUNE_VALUE_INVALID && confirmedTuneRangeHigh != TUNE_VALUE_INVALID)
        //
        // DotTune successful. Optimal tune value is midpoint of range.
        // if the midpoint is between two whole values then we round down
        //
        return (confirmedTuneRangeHigh - confirmedTuneRangeLow)/2 + confirmedTuneRangeLow;

    if (confirmedTuneRangeLow == TUNE_VALUE_INVALID) {
        //
        // no AF tune values produced a confirmation, which likely
        // means critical focus was not properly achieved prior
        // to calling this routine
        //

        // show appropriate message to user
        ;
    } else {
        //
        // we found the low end of the range but +20 still produced a focus
        // confirmation, so the high end of the range is beyond +20, meaning
        // we can't find the precise midpoint
        //
       
        // show appropriate message to user
        ;
    }
    return TUNE_VALUE_INVALID; // indicate DotTune failed
}

#43
Modules Development / Re: DotTune AFMA
February 22, 2013, 10:42:30 AM
Hey guys, this is horshack, the guy who did the DotTune YouTube video. My day job happens to be as an embedded firmware engineer so any help you think I can provide just say the word.

I looked over the source of the first commit (https://bitbucket.org/hudson/magic-lantern/commits/6cf3c98700f4b659215d14ede8d6fe15eeca2e81). In the source I see the logic uses a scoring mechanism for each tune value. The scores are 0 to 4, based on a total of 4 iterations executed for each tune value (2 inner loops x 2 outer loops). Each iteration samples 'FOCUS_CONFIRMATION' 20 times, delaying 10ms (or 10us?) between each sample, and increments the score once if any of those 20 samples yields a focus confirmation.

My first question...I assume 'FOCUS_CONFIRMATION' is either a memory-mapped variable/register driven by hardware tied to the PDAF system, or is a global variable set by some background task that is polling the PDAF hardware. If it's the latter, what is the polling frequency of that background task? I ask because broadly speaking, DotTune is biased toward a negative feedback sampling methodology, whereas the ML source implementation can interpreted as either positive or negative feedback sampling, depending on the timing properties of 'FOCUS_CONFIRMATION'. I'm just trying to correlate the user-perceptible VF confirmation feedback with how that same mechanism would be sampled from the ML implementation.

Thanks,

Horshack