Latest / Satellite image deep learning / OmniCloudMask
Transcript
- 0:05Hello,
- 0:05I'm Robin Cole,
- 0:06and welcome to the Satellite Image Deep Learning Podcast.
- 0:09SPEAKER_01: In this episode,
- 0:10SPEAKER_01: I caught up with Nick Wright to discuss Omni Cloud Mask,
- 0:12a Python library for state-of-the-art cloud and shadow masking
- 0:16in satellite
- 0:16SPEAKER_01: imagery.
- 0:18SPEAKER_01: Accurate cloud masking is crucial for reliable downstream analytics,
- 0:21SPEAKER_01: yet creating models that generalize well across different sensors,
- 0:24SPEAKER_01: resolutions,
- 0:25SPEAKER_01: and atmospheric conditions remains a significant challenge.
- 0:29Omni Cloud Mask addresses this through a novel image
- 0:31pre-processing pipeline and
- 0:32clever augmentation strategies that vary the image resolution
- 0:36presented to the
- 0:36SPEAKER_01: model.
- 0:37SPEAKER_01: Model generalization is a key concern for practitioners in our field,
- 0:41SPEAKER_01: and I found this conversation both insightful and practical.
- 0:44SPEAKER_01: I hope you do too.
- 0:46SPEAKER_01: If you're watching this on YouTube,
- 0:47SPEAKER_01: please consider liking the video and subscribing to the channel,
- 0:50SPEAKER_01: as this really helps to reach a wider audience.
- 0:52SPEAKER_01: Thank you.
- 0:54SPEAKER_01: Hi, Nick.
- 0:55SPEAKER_01: Welcome to the podcast.
- 0:56SPEAKER_00: G'day, Robin.
- 0:57SPEAKER_00: Great to be here.
- 0:58SPEAKER_01: Brilliant.
- 0:58SPEAKER_01: Let's begin with where you work and what you do.
- 1:01SPEAKER_00: Sure.
- 1:01So I'm a senior research scientist at the Department of Primary
- 1:04Industries and
- 1:05SPEAKER_00: Regional Development in Western Australia.
- 1:08SPEAKER_00: And I'm undertaking a PhD at the University of Western Australia.
- 1:14SPEAKER_01: Fantastic.
- 1:15SPEAKER_01: And what's your line of research?
- 1:17SPEAKER_00: Yeah.
- 1:18So I do a lot of deep learning,
- 1:20remote sensing stuff,
- 1:22particularly environmental work.
- 1:24SPEAKER_00: So I'm very interested in water and clouds and cloud shadows.
- 1:28SPEAKER_01: Everybody loves clouds.
- 1:30SPEAKER_01: And you've developed a state-of-the-art model for cloud detection?
- 1:34SPEAKER_00: Yeah,
- 1:35so we've recently had published the Omni Cloud Mask model and
- 1:40paper in RSE,
- 1:42which is a state-of-the-art cloud and cloud shadow model for a
- 1:46whole range of
- 1:47SPEAKER_00: different satellites and spatial resolutions and processing levels.
- 1:51And how do you train a model that's going to perform across
- 1:54different sensors and resolutions?
- 1:56SPEAKER_00: Yeah.
- 1:57SPEAKER_00: So it's, it's really interesting.
- 1:58SPEAKER_00: It's so the,
- 2:00SPEAKER_00: the approach is,
- 2:02SPEAKER_00: well,
- 2:02SPEAKER_00: I guess the problem is that different satellites have,
- 2:07SPEAKER_00: well,
- 2:08SPEAKER_00: first of all,
- 2:08SPEAKER_00: different spectral bands that they collect.
- 2:10SPEAKER_00: Right.
- 2:10And then you can receive that data and it has like different
- 2:14processing levels.
- 2:15SPEAKER_00: So it might be level one or level two and those,
- 2:18SPEAKER_00: the pixel values and those different processing levels have,
- 2:21SPEAKER_00: can have dramatically different values.
- 2:23SPEAKER_00: So,
- 2:24it's really difficult for a model to kind of understand what's
- 2:27going on in an image
- 2:28if it's trained on data from one sensor and then applied to
- 2:31another sensor because
- 2:32the pixel values are so different or the spatial scale can be
- 2:34really different.
- 2:36SPEAKER_00: So there's really two tricks that we use.
- 2:40SPEAKER_00: One is what we're calling dynamic Z-score normalization,
- 2:43which is just a way of dynamically normalizing the inputs to a
- 2:49model in a way
- 2:50SPEAKER_00: that's quite similar to
- 2:52SPEAKER_00: if you use QGIS, a local histogram stretch.
- 2:55So you're just like zooming into a small section of an image
- 2:59and just sort of
- 3:02SPEAKER_00: stretching out that image,
- 3:03SPEAKER_00: the pixel values in that image,
- 3:05so you can see the details in the dark regions and also in the
- 3:08light regions at the
- 3:09SPEAKER_00: same time.
- 3:10So we implement that as a normalization step before the model
- 3:15gets to see any data.
- 3:18SPEAKER_00: And the other thing is a...
- 3:21SPEAKER_00: dynamic resizing of the data before it's passed into the model.
- 3:26SPEAKER_00: So normally when you're training a remote sensing deep learning model,
- 3:29the batch sizes or like the dimensions of that batch of data
- 3:33that you're passing
- 3:34SPEAKER_00: into a model has fixed dimensions.
- 3:38SPEAKER_00: So you might have like a batch dimension of like two or four or 16,
- 3:43and then you've got your channels and then your X and Y
- 3:45dimensions of those images.
- 3:46And those X and Y dimensions might be like 512 by 512 or
- 3:49something,
- 3:49right?
- 3:51And that is kind of,
- 3:53that determines the spatial scale that that model is used to
- 3:58seeing.
- 3:59SPEAKER_00: And if you don't,
- 4:00so the approach to kind of teach the model to understand
- 4:06different spatial scales
- 4:08SPEAKER_00: that we use in this paper,
- 4:09it's just an augmentation that just deforms or like rescales
- 4:14those X and Y
- 4:15SPEAKER_00: dimensions.
- 4:16So this is a bit unusual because like normally when you're
- 4:19passing batches through a model,
- 4:21SPEAKER_00: All of those dimensions are static,
- 4:23but instead we're just changing them dynamically so that the
- 4:28model gets to see data
- 4:29SPEAKER_00: at a whole bunch of different spatial resolutions.
- 4:33So that combined with the normalization allows models to kind
- 4:37of work over a whole
- 4:39SPEAKER_00: bunch of different imagery types.
- 4:42SPEAKER_01: Yeah.
- 4:42And just to clarify,
- 4:43is that a zoom augmentation then or is it something else?
- 4:46SPEAKER_00: Well, sort of.
- 4:47SPEAKER_00: Yeah,
- 4:47SPEAKER_00: it's just it's an interpolation step where like,
- 4:50SPEAKER_00: so for instance,
- 4:51SPEAKER_00: if our training data is the Cloud Send 12 data set,
- 4:55SPEAKER_00: which is 10 meters Sentinel data,
- 4:58SPEAKER_00: and it starts the images at around 512 by 512 to simulate a 20 meter
- 5:05uh uh resolution image we just resample that down to 256 by 256
- 5:11and then pass that
- 5:12through the model and then we do that not at just like fixed
- 5:15resolutions but at
- 5:17every resolution between we go from 10 meters to 50 meters so
- 5:21that those spatial
- 5:23dimensions are just continually resampled while training so the
- 5:26model gets to see
- 5:27those different representations interesting and you're training
- 5:30on data from
- 5:31SPEAKER_01: multiple satellites or just from one
- 5:33SPEAKER_00: Yeah, just one, which is quite interesting.
- 5:36SPEAKER_00: So it's like, so the CloudSyn 12 data set is an amazing data set.
- 5:42And like a lot of effort has obviously gone into making it
- 5:46really robust and large
- 5:48SPEAKER_00: and spatially distributed.
- 5:51SPEAKER_00: And that's not always the case for satellites,
- 5:53SPEAKER_00: especially commercial satellites like Maxar data or planet data.
- 5:58So what we wanted to do is come up with a method where you can
- 6:03just use training
- 6:04SPEAKER_00: data from one sensor,
- 6:05but make a model that understands data from a whole bunch of
- 6:10different sensors.
- 6:11SPEAKER_00: And we've shown this in the paper.
- 6:13SPEAKER_00: It works really well.
- 6:14SPEAKER_00: We're able to get state-of-the-art results in Sentinel,
- 6:17SPEAKER_00: Level 1,
- 6:18SPEAKER_00: Level 2,
- 6:20SPEAKER_00: Landsat,
- 6:20SPEAKER_00: PlanetScope,
- 6:21SPEAKER_00: and show also that it works really well on Maxa.
- 6:24SPEAKER_01: Right.
- 6:25So you must have had a small test set for each of those
- 6:27different satellites.
- 6:29SPEAKER_00: Yeah.
- 6:29SPEAKER_00: I mean,
- 6:29SPEAKER_00: through actual development of the model,
- 6:33SPEAKER_00: we didn't actually look at output from any other resolution.
- 6:36SPEAKER_00: I just wanted to make it work really well on Sentinel.
- 6:41SPEAKER_00: And then at the end, I was like, oh, I guess we should check.
- 6:43SPEAKER_00: And it turned out to work really well.
- 6:46SPEAKER_00: And we're able to test it.
- 6:48There's some really good validation data sets,
- 6:51the CMIX data sets for both Landsat and Sentinel.
- 6:54SPEAKER_00: we were able to use as validation data sets.
- 6:57SPEAKER_00: And that's what we were using to compare our results against.
- 7:00SPEAKER_00: Yeah.
- 7:02SPEAKER_01: Fantastic.
- 7:03SPEAKER_01: And the normalization process,
- 7:05you apply that on the fly during training or do you have to
- 7:07pre-process the data
- 7:08SPEAKER_01: set and understand something about the overall data set?
- 7:11SPEAKER_00: Yeah, so that's an interesting point.
- 7:13SPEAKER_00: It's done on the fly,
- 7:15SPEAKER_00: and it's actually done after all the other augmentations,
- 7:17SPEAKER_00: which is a bit unusual.
- 7:19SPEAKER_00: Normally,
- 7:19SPEAKER_00: in these pipelines,
- 7:20SPEAKER_00: you normalize your data,
- 7:21SPEAKER_00: you apply any other spatial augmentations,
- 7:23SPEAKER_00: and then you pass the data to the model.
- 7:26But we're actually doing it last,
- 7:28which is a bit unusual,
- 7:30but it's done for a good reason.
- 7:32SPEAKER_00: So we have a range of quite drastic augmentations in our pipeline.
- 7:38to kind of make the model generalize strongly to the types of
- 7:42errors that you see
- 7:43SPEAKER_00: in remote sensing data.
- 7:44SPEAKER_00: So like tears and scenes or like scene edges and things.
- 7:50And we want the normalization to happen after that point
- 7:55because our normalization
- 7:56SPEAKER_00: approach require like,
- 7:59it uses the data within the one particular image that is
- 8:03currently being worked on.
- 8:05SPEAKER_00: You need the stats from that image.
- 8:07So if you're going to be blanking out any sections or setting
- 8:09any areas for like
- 8:10SPEAKER_00: random values,
- 8:12you actually want those statistics to be used in the
- 8:15normalization step because
- 8:17that's what will be done like at inference time when you're
- 8:19actually processing a
- 8:20SPEAKER_00: scene through.
- 8:21So actually do all the augmentations and then do the
- 8:23normalization last and then
- 8:25SPEAKER_00: pass that through the model.
- 8:26um it's actually kind of nice because um so normally well
- 8:32depending on how you're
- 8:32normalizing your data quite often you have to like ship a model
- 8:36with normalization
- 8:37statistics it's like oh here's the standard deviation and the
- 8:40mean for each of the
- 8:42SPEAKER_00: different channels um and
- 8:45SPEAKER_00: That is a bit,
- 8:45SPEAKER_00: you know,
- 8:46SPEAKER_00: you end up with these kind of sort of like magic numbers,
- 8:48SPEAKER_00: which are like not great to having code in general.
- 8:51And we can just avoid that completely just by dynamically
- 8:54creating those statistics
- 8:56SPEAKER_00: for each like patch of data that we pass through the model.
- 9:00SPEAKER_00: So it's one less thing to keep track of, which is kind of nice.
- 9:03SPEAKER_01: Absolutely.
- 9:03SPEAKER_01: Nice, nice aspect of the productionization of it.
- 9:07SPEAKER_01: And this general approach will work for other kinds of tasks.
- 9:12SPEAKER_00: Yeah, so that was a big question that we kind of came to at the end.
- 9:17SPEAKER_00: It's like,
- 9:18SPEAKER_00: well,
- 9:18does this just work for clouds and cloud shadows because
- 9:21they're big and fluffy and
- 9:22SPEAKER_00: have heaps of texture?
- 9:24And so that was something that we were looking into just after
- 9:28this paper was published.
- 9:29SPEAKER_00: And there's a preprint out of the next paper,
- 9:32SPEAKER_00: which we're calling Omni Water Mask,
- 9:35SPEAKER_00: where we apply the same techniques
- 9:37to train a sensor agnostic water detection model using this
- 9:42time imagery from the
- 9:45SPEAKER_00: S1,
- 9:46SPEAKER_00: S2 water data set and the FLARE data set.
- 9:49SPEAKER_00: They're very different data sets.
- 9:51You're dealing with drone imagery on one end and central
- 9:53imagery on the other.
- 9:55And we're able to train up this model that understands sort of
- 9:59how to identify
- 10:00SPEAKER_00: water over this wide range of spatial scales.
- 10:03SPEAKER_01: Yeah.
- 10:04SPEAKER_00: So, yeah, I guess getting to your point,
- 10:07SPEAKER_00: Yes,
- 10:07SPEAKER_00: we think it works over a whole range of different problems,
- 10:10SPEAKER_00: not just clouds and cloud shadows.
- 10:12And in my mind,
- 10:13I'm thinking you've already got a model that works well for
- 10:15clouds.
- 10:15Can you fine tune that for water or do you need to start again
- 10:18from scratch?
- 10:19SPEAKER_00: Yeah.
- 10:20SPEAKER_00: Yeah.
- 10:21So for the particular water model,
- 10:24we found that we needed to use a significantly larger model.
- 10:28SPEAKER_00: And I'm not entirely sure why that was the case.
- 10:33SPEAKER_00: So no, we didn't in that case.
- 10:34SPEAKER_00: But you can just use pre-trained models.
- 10:36So we're just starting with ImageNet pre-trained weights and
- 10:41then just going from there.
- 10:42SPEAKER_00: And it seems to work quite well.
- 10:45SPEAKER_00: Yeah, so just starting with ImageNet weights seems to work reasonably.
- 10:48SPEAKER_00: But yeah, there's probably some value in that.
- 10:51SPEAKER_00: Obviously, there's a lot of information in that Cloud Zone 12 data set.
- 10:54SPEAKER_00: So using that as a pre-training step for other problems.
- 10:59SPEAKER_00: Sounds pretty reasonable to me.
- 11:00SPEAKER_01: Yeah.
- 11:00And there's obviously lots of interest at the moment in
- 11:02foundational models,
- 11:03SPEAKER_01: which are also trained on large data sets.
- 11:05But I guess clouds are kind of similar to the task there where
- 11:10you're trying to
- 11:11SPEAKER_01: impute missing information.
- 11:12SPEAKER_01: Maybe that's a general task, putting a cloud in and detecting it.
- 11:18SPEAKER_00: Yeah,
- 11:18I guess there has been a bit of a trend recently of papers
- 11:22which try to avoid the
- 11:24SPEAKER_00: whole cloud marking problem by
- 11:26SPEAKER_00: training a model with Cloudy data and just like,
- 11:29SPEAKER_00: well,
- 11:29SPEAKER_00: you know,
- 11:29SPEAKER_00: the model's gonna see this in production,
- 11:31so let's just train the model with it and kind of try and get a
- 11:34robust model,
- 11:36SPEAKER_00: which I think is a totally fine approach.
- 11:39SPEAKER_00: Like, yeah, I think so.
- 11:42SPEAKER_00: It does,
- 11:42SPEAKER_00: obviously it makes the problem harder,
- 11:44SPEAKER_00: but as long as your model kind of can understand that,
- 11:47SPEAKER_00: I guess it's just like a strong augmentation,
- 11:49SPEAKER_00: right?
- 11:51SPEAKER_01: And the Cloud 1012 data set is a large data set, of course.
- 11:55Yeah,
- 11:56so I think the hand-labelled section of that is about 10,000
- 12:00images.
- 12:01SPEAKER_00: I think around 9,000 of those are the training set,
- 12:06SPEAKER_00: and then the rest is split up to validation and testing.
- 12:09SPEAKER_01: Yeah,
- 12:10and did you also evaluate the general generalisation approach
- 12:13for smaller data sets
- 12:14SPEAKER_01: and tasks?
- 12:17SPEAKER_00: Yeah, so when we were... Actually, that's a good point.
- 12:22SPEAKER_00: So I was curious to see if
- 12:25dynamic Z-score normalization just works on computer vision
- 12:27problems in general.
- 12:30I was playing around with this the other weekend on a dog
- 12:34breeds classification
- 12:36problem and just swapped out the normalization from just like
- 12:41the standard divide
- 12:41SPEAKER_00: by 256 with dynamic Z-score normalization.
- 12:45SPEAKER_00: And yeah, you end up with the exact same results.
- 12:48SPEAKER_00: So I'd be interested to know in what situations it doesn't work.
- 12:51SPEAKER_00: I guess one thing,
- 12:52SPEAKER_00: if people are implementing this themselves,
- 12:55one thing to keep in mind is you need to be careful of no data
- 13:00values in your
- 13:00SPEAKER_00: scenes.
- 13:01SPEAKER_00: So you,
- 13:03SPEAKER_00: well,
- 13:03SPEAKER_00: at least in our approach,
- 13:05SPEAKER_00: we don't use values from the no data regions of scenes to calculate the
- 13:11SPEAKER_00: normalization statistics.
- 13:14SPEAKER_00: Yeah,
- 13:14we just forget about them when you're normalizing and then just
- 13:19set them to zero
- 13:21SPEAKER_00: before passing those areas through the model.
- 13:23SPEAKER_00: And that seems to work.
- 13:26SPEAKER_00: But yeah, I'd be interested.
- 13:28SPEAKER_00: I'm sure there are,
- 13:31alternatives here and so like when we're normalizing this data
- 13:35we're normalizing it
- 13:36uh each channel individually as well so each i guess for each
- 13:42patch of image that
- 13:43goes into the model we're setting the mean and standard we're
- 13:46setting the mean to
- 13:47zero and the standard deviation to one um and we're doing that
- 13:50for each band so the
- 13:53model to a certain extent like if you visualize the output of
- 13:56that everything
- 13:58just looks mostly gray which is kind of interesting so you kind
- 14:03of lose some you're
- 14:06obviously losing some color information um which you would
- 14:10think would make the
- 14:11model worse but i guess the thing is you can't rely on those
- 14:17ratios to be static in
- 14:19SPEAKER_00: the in the real world when you're passing
- 14:22data from different sensors,
- 14:23like one sensor,
- 14:25like red channel just might be higher or lower.
- 14:27So being able to just come up with a kind of a function which
- 14:33might lose some data,
- 14:35SPEAKER_00: but at least gives you a consistent output is useful.
- 14:38SPEAKER_01: Yeah.
- 14:38I suppose another approach would be to calculate something like
- 14:40a water index,
- 14:41SPEAKER_01: which is normalized implicitly and use that.
- 14:48SPEAKER_00: Yeah.
- 14:49SPEAKER_00: Yeah.
- 14:50SPEAKER_00: Yeah, yeah, yeah.
- 14:51SPEAKER_00: That's interesting you bring that up.
- 14:52SPEAKER_00: That's part of the Omni water mask paper actually uses...
- 15:01We use output of a deep learning model and OSM data to actually
- 15:06work out locally
- 15:08SPEAKER_00: optimal normalized difference water index.
- 15:11SPEAKER_00: You know how you end up with that problem where...
- 15:15SPEAKER_00: If you get a really good threshold value for a normalized water index,
- 15:19SPEAKER_00: you can segment water really well.
- 15:22SPEAKER_00: But working out those values is really difficult.
- 15:26The core part of that paper is actually using deep learning
- 15:29outputs and OSM data,
- 15:32SPEAKER_00: so like water polygons,
- 15:33SPEAKER_00: community-sourced water polygons,
- 15:35SPEAKER_00: to actually automate the finding of locally optimal thresholds.
- 15:40right so you know you know where the water is you can just tune
- 15:43your threshold
- 15:44until you get the best overlap exactly because like water is
- 15:47relatively static like
- 15:48excluding like floods and things like if there's a pond there
- 15:52like two days ago
- 15:53it's probably still there yeah yeah so you can kind of take
- 15:56advantage of that um i
- 15:59SPEAKER_00: guess one other approach which is expensive to do is um like histogram
- 16:04SPEAKER_00: normalization
- 16:05which people have done as a normalization set,
- 16:08which is a bit similar,
- 16:09right?
- 16:09Because you're just like normalizing based on the values in
- 16:12that particular patch
- 16:13SPEAKER_00: that you're passing in.
- 16:15SPEAKER_00: But it's just expensive to do on, especially on like 16-bit data.
- 16:19SPEAKER_00: There's just a lot of calculations that you need to do.
- 16:22SPEAKER_01: Yeah.
- 16:22SPEAKER_01: A lot of pre-processing.
- 16:24SPEAKER_01: Probably don't want to do that while you've got a GPU waiting for you.
- 16:27SPEAKER_01: Exactly.
- 16:28SPEAKER_01: Yeah.
- 16:29SPEAKER_01: Well, really fascinating topic.
- 16:30SPEAKER_01: You've released the code and the paper's out.
- 16:33SPEAKER_01: Where do we find those?
- 16:35SPEAKER_00: Yeah.
- 16:35So you can just,
- 16:36if you just Google Omni Cloud Mask,
- 16:39the GitHub page will pop up.
- 16:40SPEAKER_00: You can just dip install or UV add Omni Cloud Mask.
- 16:45SPEAKER_00: And then it's just, it's just a single function.
- 16:47SPEAKER_00: You can just,
- 16:48SPEAKER_00: you can either pass in an array,
- 16:51SPEAKER_00: which is like a,
- 16:53SPEAKER_00: so the method requires the red,
- 16:57SPEAKER_00: green,
- 16:57SPEAKER_00: and near infrared bands,
- 16:58because we found those to be the most important for cloud and
- 17:01cloud shadow
- 17:02SPEAKER_00: detection.
- 17:03So you do need the near-up for red band,
- 17:05which can be a little annoying sometimes.
- 17:08SPEAKER_00: But, you know, most satellites have it.
- 17:11SPEAKER_00: Yeah, so you don't need the blue because it didn't seem to help.
- 17:14SPEAKER_00: So we didn't need it.
- 17:16SPEAKER_00: So you can just pass in those as a numpy array.
- 17:18And then if you've got a GPU,
- 17:20you should expect a result back pretty quick smart.
- 17:22SPEAKER_00: I think if you're throwing in a Sentinel image,
- 17:27SPEAKER_00: you can process it at,
- 17:29SPEAKER_00: there's a flag for processing at 10 or 20 metres res.
- 17:32SPEAKER_00: If you've got a,
- 17:33SPEAKER_00: modern GPU,
- 17:35it's about nine seconds to do a scene at 10 meters or about two
- 17:38seconds to do it at
- 17:40SPEAKER_00: 20 meter res.
- 17:42SPEAKER_00: The other function that the library has
- 17:46SPEAKER_00: is if you've got a whole bunch of scenes,
- 17:49there's a slightly optimized version of it where you can feed
- 17:53in a list of
- 17:54directories and it synchronizes loading the data and
- 17:58pre-processing it while the
- 17:59SPEAKER_00: GPU is working.
- 18:00SPEAKER_00: So if you've got a whole bunch of scenes to do,
- 18:03SPEAKER_00: we can kind of speed that processing up quite a bit.
- 18:07So there's two ways to do it,
- 18:09just depending on kind of how the workflow is.
- 18:11SPEAKER_00: So
- 18:13the github link is dpird dna slash omnicloud mask if you google
- 18:22that it should pop
- 18:22up and there's some example notebooks in there to play around
- 18:26with fantastic i'll
- 18:27put the link in the show notes one last thing if people want to
- 18:30follow your updates
- 18:31SPEAKER_01: which is the best platform for them to do that
- 18:34SPEAKER_00: Oh, that's a good one.
- 18:35SPEAKER_00: Probably the GitHub page, honestly.
- 18:39Either mine or the Omni Cloudmass page itself is probably the
- 18:44easiest place to find things.
- 18:45SPEAKER_01: Fantastic.
- 18:46SPEAKER_01: Well, thanks for joining me today.
- 18:47SPEAKER_01: Fascinating topic.
- 18:49SPEAKER_01: And I wish you all the best for the remainder of your PhD.
- 18:52SPEAKER_00: Thanks a lot, Robin.
- 18:52SPEAKER_00: Appreciate it.
- 18:53SPEAKER_01: Yeah.