Auto ETTR on EOSM

Started by Morghus, March 04, 2014, 12:46:37 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Morghus

Alright since I wanted to use my EOS M as a secondary timelapse camera I wanted to fix ETTR on it and finally dive into ML hacking. Here are a few things I discovered so far:


  • auto_ettr_step_lv_slow() and auto_ettr_on_request_task_slow() work, the quick fix would be to use them instead of the *_fast functions, but that's not what I want
  • the *_fast functions use CBR_VSYNC_SETPARAM to modify the exposures for each frame
  • in auto_ettr_vsync_cbr() the exposure is adjusted for each frame and on my 6D changes are visible immediately, on the M however, exposure is smoothly changed after around 1 second of delay, get_frame_iso() and get_frame_shutter_timer() still return the old value from the previous frame even though set_frame_iso() and set_frame_shutter_timer() have been called
  • the *_fast functions expect the exposure to change within 2 frames, it does not happen for some reason and so it over-compensates in each step
  • HDR video's hdr_step() uses a similar mechanism to change the exposure for each frame, although not exactly the same because it's not a module, ISO seems to change correctly and immediately for each frame

I'm stuck here... the quick fix would be to use the *_slow functions obviously, but it must be possible to fix auto_ettr_vsync_cbr(). I'd really appreciate it if someone pointed me into the right direction as this is my first time diving into ML code and I have no clue where to start. Is it possible that LV just behaves differently in movie mode? Can I put the camera into this mode if that's true? What other possibilities should I look into?

a1ex

That's a great start. Indeed, the key to fast ETTR in LiveView is to be able to change shuter speeds via FRAME_SHUTTER_TIMER (simply by setting it to some constant value from a vsync CBR). Also see this explanation: http://www.magiclantern.fm/forum/index.php?topic=9741.msg101808#msg101808

So, I would start by checking Canon's values at different shutter speeds, but considering EOS-M's quirks, I would do that while recording H.264. Then, you can cross-check the values with standby and photo modes, take into account the ExpSim mechanism, and see if you can get something useful out of it. In any case, FRAME_BV should indicate the overall exposure (8 units = 1 EV), and changes in FRAME_SHUTTER_TIMER should be consistent with changes in FRAME_SHUTTER. So, feel free to create a table with some values from these macros.

You may need to program directly in the core to access these macros, or copy their definition in the module while investigating.

Next, you should try to override these things; for ETTR, we are interested in overriding ISO (FRAME_ISO) and shutter speed (FRAME_SHUTTER_TIMER, which behaves like 1/exposure_time).

If it works, but it's changing smoothly, something's fishy. You might try to use exposure override, double-check the vsync input/state in the state transition (but this one looks OK from here), or hexdump the memory starting from VIDEO_PARAMETERS_SRC_3 and try to identify some more exposure variables (change Canon settings and see which offsets change).

Morghus

Thanks for your guidance so far, I did what you suggested:



I'm not really sure what happens there though. I can see where FRAME_ISO, FRAME_APERTURE, FRAME_SHUTTER and FRAME_SHUTTER_TIMER come from but setting FRAME_ISO or FRAME_SHUTTER_TIMER does not seem to have an effect. Every time the digit left of FRAME_BV changes, FRAME_ISO is set to ISO_100 or ISO_800 and you can see it change in the dump for a frame, LV does not seem to be affected though.

It's only smoothing in photo mode, in movie mode or with Expo. Override on it does not smooth the changes. Any other suggestions where I can poke at?




a1ex

So... setting FRAME_ISO and FRAME_SHUTTER_TIMER have no effect, but HDR video works. To be clear:

- did you try HDR video with ISO or also the advanced one with shutter?
- are these two things working in movie mode?
- are these two things working in expo override mode?

If the last answer is yes, you could try enabling expo override temporarily, from auto_ettr_prepare_lv. Look for "workaround for Canon's manual lens underexposure bug" and try using that for photo mode.

There is also a test in AUTO_ETTR_DEBUG; that one tests the vsync mechanism and checks if the exposure is indeed altered as requested (e.g. you request darkening by X EV, you get an image actually darker by X EV +/- a small measurement error).

This test should probably be moved to core code, in Debug menu, or maybe in raw_diag, to check this backend support everywhere (would cover LV RAW histogram, set_frame_iso/set_frame_shutter_timer and the vsync hook).

Morghus

Quick update to answer those three questions:



Expo override does not fix it.
While HDR video is doing its thing I can not see the changes it should be doing in the dump... I'll try that AUTO_ETTR_DEBUG test now.

a1ex

You don't see the changes because the module code runs before hdr video, and these things are reset by Canon code for each frame.

ETTR code actually assumes the exposure variables are reset by Canon code each time.

Morghus

I tried that AUTO_ETTR_DEBUG block and I assume those blue dots should be on the diagonal red line? They are all horizontal and when the scene changes they go up and down.

I also tried setting ISO and shutter in each call now, no effect. Just for clarificaion, the Advanced Bracketing/Movie HDR code runs after the vsync hook has been called? What is done in between? It seems to work for those two things and those are not modules with vsync hooks.

a1ex

Yes, the dots should be on the diagonal red line.

Advanced bracketing is for photos (does not use vsync).

Between HDR video (hdr_step) and your vsync hook there may be... other vsync hooks from other modules. The module vsync code is actually called from hdr_step.

Morghus

That is useful information, I didn't know what that line was doing in hdr_step() but I couldn't find any other references to that, so it makes sense that the module callback is called from in there.

I did a quick test in hdr_step(), changing the ISO and shutter timer in some intervals and it only works while recording h264. Movie mode and not recording, or photo mode => no dice.


Edit: I moved the same code to the ettr callback and it seems to work there too when recording... I'm not sure why it didn't work yesterday, must have been something I missed

a1ex

You could try playing with the state object hooks; it's mostly trial and error. This vsync hook runs in Canon's LiveView task (EVF), which is this one: http://a1ex.bitbucket.io/ML/states/EOSM-alt/sm28.htm

I suggest trying all the inputs from state 5 that do not change the state.

Another possibility is to rewrite the backend in order to override shutter via ADTG. This one should be a little more portable but we need to find a hook point for it first (on 5D3 I found something in DebugMsg, but it's not exactly portable and was not designed as a replacement for FRAME_SHUTTER_TIMER, so it may need some major rewrite).

Morghus

I tried setting the ISO in all inputs of state 5, no dice... it works when starting to record a movie, but outside of that it doesn't. At least I think I tried it. As far as I know this code should do that:

state-object.c:
   
...
#ifdef EVF_STATE

    if (self == EVF_STATE && old_state == 5)
    {
    int iso = vsync_counter % 2 ? 72 : 96;
    set_frame_iso(iso);
    NotifyBox(1000, "%d", iso);
    }

...



a1ex

What about:


if (self == EVF_STATE)
    {
    int iso = rand() % 2 ? 72 : 96;
    set_frame_iso(iso);
    //NotifyBox(1000, "%d", iso); /* don't enable this one, since it may slow down liveview */
    }


If that one works, all it's left is to narrow down the state/transition pair.

Morghus

Nope, same behavior, no flickering until I start recording a movie, and flickering stops when recording stops :( If my understanding is right, the evfSetParamInterrupt input is called/spied on when LV is ready to accept new exposure params in the memory addresses?

a1ex

Yap.

In this case... I'm afraid the only solution is to do this via ADTG. Will push a workaround meanwhile so it falls back to the slow method.

Let me know if this works: https://bitbucket.org/hudson/magic-lantern/commits/a6ff18c6aa44

Morghus

It works for Always ON and HalfS DblClick, Auto Snap and using ETTR with the intervalometer doesn't seem to work, maybe something goes wrong while analyzing the image in image review? (I set it to 2 sec) Also, the Press SET trigger doesn't work since the M has a combined Q/SET button, maybe we can remove this entry for the M.

a1ex

Probably it's worth checking button codes; do you get a BGMT_PRESS_SET event in LiveView?

If half-shutter trigger is working, I guess the preconditions are all OK, so... the only thing left is the button code, no?

1%

Photo mode and not recording H264 are actually the same as 60P just cut down to 30 fps. I could have sworn that the shutter fine tuning worked at rest though.

Morghus

in auto_ettr_keypress_cbr
if (!display_idle()) return 1;
causes it to stop handling the event when Q/SET is pressed, even though the event gets there correctly with the right codes... this is because when you press that button, the Q menu comes up
if (key == MODULE_KEY_PRESS_SET) {
    NotifyBox(100, "SET PRESSED %d %d %d", AUTO_ETTR_TRIGGER_BY_SET, key == MODULE_KEY_PRESS_SET, key);
}

works before the display_idle check, not after

a1ex

This means the Q menu already came up before the SET event (so it must be some other event).

Can you find that event and try to block it? (that is, return 0 when you receive that event)

1%

same behavior both ways, on to something

Morghus

Alright that took a while... Since gui.h contains this
#define BGMT_Q TOUCH_1_FINGER
and key codes are mapped to different values in module.c (and unknown ones mapped to 0) I couldn't access the key code in the module. By outputting the pressed raw keycodes in module.c I was able to find out that blocking 0x1d in handle_module_keys prevents the quick menu from being opened, but that also blocks the BGMT_PRESS_SET event for some reason. However, it does not block the BGMT_UNPRESS_SET event so listening for that to start ETTR I could get it to work by also translating the 0x1d key code to a module code. While ETTR is enabled and set to Press SET, the quick menu becomes inaccessible though... would that be preferable to not being able to use SET as a trigger? I can clean up the code and submit a pull request if yes.

1%

does ETTR + intervalometer work after the fix? I'm only able to use it in TV/AV not M... it just never adjusts in the QR

Morghus

No it doesn't work there yet, something doesn't work after analyzing the photo in image review, it should work in manual always on or with halfs dblclick

a1ex

In "always on", whatever it does in QR will be canceled as soon as you get back to LiveView.

This probably needs re-thought for EOS-M; on other cameras, this mode is meant to be used outside LiveView.

Morghus

So a CONFIG_IS_MIRRORLESS or CONFIG_EVF_ONLY or something should be introduced and then disable the parts where it tries to change things outside LV? I figure the EOS M and M2 won't be the last mirrorless cameras from canon and other ones probably should behave like the M.

Also:
Quote from: Morghus on March 05, 2014, 11:11:41 PM
While ETTR is enabled and set to Press SET, the quick menu becomes inaccessible though... would that be preferable to not being able to use SET as a trigger? I can clean up the code and submit a pull request if yes.
Where would be the right place to discuss this?

a1ex

Yes, sounds good. You will need to export it to modules as a function though (since camera-specific ifdef's won't end up in module code).

I'm not sure what to suggest about SET (without trying, it's hard to evaluate how it feels like, and I don't know how essential is the Q menu on the M). Maybe sketching up some UI scenarios or maybe a mockup could be useful.

Another button... I guess it's unlikely to find one. Gestures would be probably cool, but not sure how practical (not to mention the implementation). An icon where you could tap? no idea, never tried a touch-screen camera.

Morghus

Made a pull request, double tapping the screen will trigger ETTR.

I also found out why ETTR in combination with the intervalometer doesn't work: in auto_ettr_step() the following line fails:

    if (lens_info.raw_shutter == 0 && is_m) return;

raw_shutter seems to be 0, right before taking a photo it is set correctly, something seems to set it to 0 while or after taking a photo... I tried commenting out that line and it seems to fix it but I think corrections aren't always dead on, it takes one or two photos to settle fully.

a1ex

Will check it, thanks.

In M mode, auto_ettr_work_m starts with tv = lens_info.raw_shutter. If that one is not valid (does not contain your current shutter speed), the math will be broken. Normally, on other cameras, it happens while flash is recharging, but not in QR. Maybe you can have a loop that prints this value to see whether we may need to wait or something?

So... were you able to turn off LiveView on the M? I've asked somewhere else and IIRC it wasn't possible.

Morghus

Quote from: a1ex on March 07, 2014, 07:42:14 AM
So... were you able to turn off LiveView on the M? I've asked somewhere else and IIRC it wasn't possible.
NotifyBox(1000, "%d %d ", lv, raw_lv_is_enabled());

Pressing INFO cycles through these modes:

I'll test lens_info.raw_shutter next!

a1ex

Yap. Can you take pictures from that screen with photo settings without going back to LiveView?

(this is what ETTR expects in Always On / Auto Snap settings outside LiveView)

Morghus

No, unfortunately as soon as you press half shutter it goes back to LV

Morghus

I tried a few things:

  • lens_info.raw_shutter regains a value around 1 second after QR popped up
  • In PROP_HANDLER(PROP_GUI_STATE) I can't wait for that value because lens_info.raw_shutter only regains a value after this function returned (I actually tried this, as if I waited 2 seconds it took exactly 2 seconds for raw_shutter to get a value)
  • I tried creating a task with TASK_CREATE() and waiting for raw_shutter there, same behavior as if I just wait in PROP_HANDLER(PROP_GUI_STATE)
  • vsync_cbr is not called while QR is active, so I can't check raw_shutter there and, if necessary, start ETTR
  • debug_task sees raw_shutter regain a value before QR is over

I don't know if I should create a background task in ettr that always runs, like the vsync_cbr, checking for the right conditions... would be a waste of resources. I don't know if there are any background tasks that I could use for that, any suggestions?

a1ex

What about something like this?


diff -r 811c68f776a5 modules/ettr/ettr.c
--- a/modules/ettr/ettr.c Thu Mar 06 22:29:09 2014 +0200
+++ b/modules/ettr/ettr.c Fri Mar 07 23:57:52 2014 +0200
@@ -620,6 +620,16 @@

static int auto_ettr_work(int corr)
{
+    if (it's going to call auto_ettr_work_m)
+    {
+        /* in M mode, wait until shutter speed is reported by Canon firmware */
+        while (lens_info.raw_shutter == 0)
+        {
+            msleep(50);
+            /* add some timeout handling, e.g. 1-2 seconds */
+        }
+    }
+
     /* save initial exposure settings so we can print them */
     char* expo_settings = get_current_exposure_settings();
     snprintf(prev_exposure_settings, sizeof(prev_exposure_settings), "%s", expo_settings);
@@ -699,7 +709,6 @@
     if (shooting_mode != SHOOTMODE_M && shooting_mode != SHOOTMODE_AV && shooting_mode != SHOOTMODE_TV && shooting_mode != SHOOTMODE_P && shooting_mode != SHOOTMODE_MOVIE) return;
     int is_m = (shooting_mode == SHOOTMODE_M || shooting_mode == SHOOTMODE_MOVIE);
     if (lens_info.raw_iso == 0 && is_m) return;
-    if (lens_info.raw_shutter == 0 && is_m) return;
     if (auto_ettr_running) return;
     if (is_hdr_bracketing_enabled() && !AUTO_ETTR_TRIGGER_BY_SET) return;


Basically, I've moved that check in the background task created from the prop handler; there you don't have any restrictions regarding waiting, and there should be no side effects for the other cameras.

1%

why is it instant in av/tv?

a1ex

In av/tv it only has to adjust exposure compensation; in M it decides shutter and ISO.

Imagine how it would look if it would also decide aperture.

Morghus

Works perfectly now! I added a timeout and a status, how does this look?

diff -r 811c68f776a5 modules/ettr/ettr.c
--- a/modules/ettr/ettr.c Thu Mar 06 22:29:09 2014 +0200
+++ b/modules/ettr/ettr.c Fri Mar 07 23:19:25 2014 +0100
@@ -40,6 +40,7 @@
#define AUTO_ETTR_TRIGGER_BY_HALFSHUTTER_DBLCLICK (auto_ettr_trigger == 3)

/* status codes */
+#define ETTR_EXPO_PRECOND_TIMEOUT -2
#define ETTR_EXPO_LIMITS_REACHED -1
#define ETTR_NEED_MORE_SHOTS 0
#define ETTR_SETTLED 1
@@ -620,6 +621,21 @@

static int auto_ettr_work(int corr)
{
+    if (shooting_mode == SHOOTMODE_M || shooting_mode == SHOOTMODE_MOVIE)
+    {
+        /* in M mode, wait until shutter speed is reported by Canon firmware */
+        int waited = 0;
+        while (lens_info.raw_shutter == 0)
+        {
+            if (waited > 2000)
+            {
+                return ETTR_EXPO_PRECOND_TIMEOUT;
+            }
+            msleep(50);
+            waited += 50;
+        }
+    }
+
     /* save initial exposure settings so we can print them */
     char* expo_settings = get_current_exposure_settings();
     snprintf(prev_exposure_settings, sizeof(prev_exposure_settings), "%s", expo_settings);
@@ -671,6 +687,13 @@
         msleep(1000);
         bmp_printf(FONT_MED, 0, os.y0, "ETTR: expo limits reached\n%s", get_current_exposure_settings());
     }
+    else if (status == ETTR_EXPO_PRECOND_TIMEOUT)
+    {
+        beep_times(3);
+        ettr_pics_took = 0;
+        msleep(1000);
+        bmp_printf(FONT_MED, 0, os.y0, "ETTR: timeout while waiting for preconditions\n");
+    }
     else if (AUTO_ETTR_TRIGGER_AUTO_SNAP)
     {
         /* take another pic */
@@ -699,7 +722,6 @@
     if (shooting_mode != SHOOTMODE_M && shooting_mode != SHOOTMODE_AV && shooting_mode != SHOOTMODE_TV && shooting_mode != SHOOTMODE_P && shooting_mode != SHOOTMODE_MOVIE) return;
     int is_m = (shooting_mode == SHOOTMODE_M || shooting_mode == SHOOTMODE_MOVIE);
     if (lens_info.raw_iso == 0 && is_m) return;
-    if (lens_info.raw_shutter == 0 && is_m) return;
     if (auto_ettr_running) return;
     if (is_hdr_bracketing_enabled() && !AUTO_ETTR_TRIGGER_BY_SET) return;

Morghus


a1ex

Great! Will try to polish them and merge later today or tomorrow, along with a few others in the queue that I have yet to try myself.