Sunday 7 August 2016

post processing - How can I automatically digitally remove shifting sensor dust speckles from a large series of photos?


While in the desert, my sensor became dusty. I was out for weeks and did not notice this until the end of my trip, so I have hundreds of photos with sensor dirt. Throughout the vacation, dust gradually got worse as I frequently changed lenses. After my vacation, I had the sensor professionally cleaned and new photos have no spots, but I'm looking for a solution for the vacation photos.


To cleanup my photos, I used The Gimp to produce a mask of the locations of the worst dust pieces:


worst dust locations


then used gmic to remove them from the commandline:



gmic 08-34-51.jpg ../../../dustmask_20180606_sky_flat_neg.png +inpaint_patchmatch[0] [1],10,7,4,2,1,0 -o[2] 08-34-51-clean.jpg

Although this is not an optimal solution because it essentially repaints those areas from scratch (without using the background information), it provides satisfatory results in the blue sky. I figured that this way, I could automate the process over the hundreds of images affected. Unfortunately, the dust is not stationary so this doesn't work. It shifts around by the order of 20 pixels, which corresponds to about 80 µm. I don't know if this is due to the dust physically shifting around, or if it may be due to in-camera image processing such as image stabilisation or lens correction. I have taken some images in both raw and JPEG, and for those images I can visually see a slight shift in the shape of the image.


It is very subtle, but if you compare the following two images, the largest and most prominent piece of dust has moved from being centred on pixel (1677, 619) to being centred on pixel (1655, 603). On the other end, a less prominent pixel on the right has moved from (4919, 1485) to (4940, 1483). Perhaps it shouldn't be too surprising that pieces of dust can and do shift around relative to the sensor?


Image 1 with dust in the sky


Image 2 with dust in the sky


The photos are taken roughly 90 minutes apart. The images I posted are downsampled by a factor 2.


The EXIF data include Sony Makernote data, which may include information on image stabilisation, such as ImageStabilization = 1. I don't know if it includes further details on image stabilisation. It does include a field Sony_0x201f = 128 17 2 0, which appears to vary between images, but is identical between the two images shown above, so it does not (uniquely) identify information to correct for image stabilisation. I don't know if any other field does. The full exif data (as reported by exiftool -v) for one image shows:



ExifToolVersion = 10.10

FileName = 08-35-11.jpg
Directory = .
FileSize = 9440161
FileModifyDate = 1528067625
FileAccessDate = 1529519016
FileInodeChangeDate = 1528150970
FilePermissions = 33204
FileType = JPEG
FileTypeExtension = JPG
MIMEType = image/jpeg

JPEG APP1 (44908 bytes):
ExifByteOrder = II
+ [IFD0 directory with 13 entries]
| 0) ProcessingSoftware = digiKam-5.6.0
| 1) ImageDescription =
| 2) Make = SONY
| 3) Model = ILCE-6000
| 4) Orientation = 1
| 5) XResolution = 350 (350/1)
| 6) YResolution = 350 (350/1)

| 7) ResolutionUnit = 2
| 8) Software = ILCE-6000 v3.20
| 9) ModifyDate = 2018:05:11 08:35:11
| 10) YCbCrPositioning = 2
| 11) ExifOffset (SubDirectory) -->
| + [ExifIFD directory with 38 entries]
| | 0) ExposureTime = 0.003125 (1/320)
| | 1) FNumber = 11 (110/10)
| | 2) ExposureProgram = 3
| | 3) ISO = 100

| | 4) SensitivityType = 2
| | 5) RecommendedExposureIndex = 100
| | 6) ExifVersion = 0230
| | 7) DateTimeOriginal = 2018:05:11 08:35:11
| | 8) CreateDate = 2018:05:11 08:35:11
| | 9) ComponentsConfiguration = 1 2 3 0
| | 10) CompressedBitsPerPixel = 3 (3/1)
| | 11) BrightnessValue = 10.76328125 (27554/2560)
| | 12) ExposureCompensation = 0 (0/10)
| | 13) MaxApertureValue = 3.6171875 (926/256)

| | 14) MeteringMode = 5
| | 15) LightSource = 0
| | 16) Flash = 16
| | 17) FocalLength = 18 (180/10)
| | 18) MakerNoteSony (SubDirectory) -->
| | + [MakerNotes directory with 94 entries]
| | | 0) Sony_0x1003 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
| | | 1) Sony_0x2000 = 0
| | | 2) PreviewImage =
| | | 3) Rating = 0

| | | 4) Sony_0x2003 =
| | | 5) Contrast = 0
| | | 6) Saturation = 0
| | | 7) Sharpness = 0
| | | 8) Brightness = 0
| | | 9) LongExposureNoiseReduction = 1
| | | 10) HighISONoiseReduction = 2
| | | 11) HDR = 0 0
| | | 12) Sony_0x200c = 0 0 0
| | | 13) Sony_0x200d = 1 (10/10)

| | | 14) WBShiftAB_GM = 0 0
| | | 15) ShotInfo (SubDirectory) -->
| | | + [BinaryData directory, 390 bytes]
| | | | FaceInfoOffset = 94
| | | | SonyDateTime = 2018:05:11 08:35:11
| | | | SonyImageHeight = 4000
| | | | SonyImageWidth = 6000
| | | | FacesDetected = 0
| | | | FaceInfoLength = 37
| | | | MetaVersion = DC7303320222000

| | | 16) CreativeStyle = Standard
| | | 17) ColorTemperature = 0
| | | 18) ColorCompensationFilter = 0
| | | 19) SceneMode = 0
| | | 20) ZoneMatching = 0
| | | 21) DynamicRangeOptimizer = 3
| | | 22) ImageStabilization = 1
| | | 23) ColorMode = 0
| | | 24) FullImageSize = 4000 6000
| | | 25) PreviewImageSize = 1080 1616

| | | 26) FileFormat = 3 3 1 0
| | | 27) Quality = 2
| | | 28) FlashExposureComp = 0 (0/10)
| | | 29) WhiteBalanceFineTune = 0
| | | 30) WhiteBalance = 0
| | | 31) SonyModelID = 312
| | | 32) Teleconverter = 0
| | | 33) MultiFrameNoiseReduction = 0
| | | 34) PictureEffect = 0
| | | 35) SoftSkinEffect = 0

| | | 36) VignettingCorrection = 2
| | | 37) LateralChromaticAberration = 2
| | | 38) DistortionCorrectionSetting = 0
| | | 39) Sony_0x2015 = 65535
| | | 40) LensType = 65535
| | | 41) LensSpec = ...5c
| | | 42) AutoPortraitFramed = 0
| | | 43) FlashAction = 0
| | | 44) Sony_0x2018 = 0
| | | 45) Sony_0x2019 = 0

| | | 46) Sony_0x201a = 1
| | | 47) FocusMode = 2
| | | 48) AFAreaModeSetting = 1
| | | 49) FlexibleSpotPosition = 0 0
| | | 50) AFZoneSelected = 0
| | | 51) Sony_0x201f = 128 17 2 0
| | | 52) AFPointsUsed = 0 0 0 0 0 0 0 0 0 0
| | | 53) Sony_0x2021 = 0
| | | 54) FocalPlaneAFPointsUsed = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
| | | 55) Sony_0x2023 = 0

| | | 56) Sony_0x5001 = 0 (0/1000)
| | | 57) Sony_0x5002 = 128
| | | 58) ExposureMode = 7
| | | 59) Sony_0xb045 = 0
| | | 60) Sony_0xb046 = 0
| | | 61) JPEGQuality = 65535
| | | 62) FlashLevel = 0
| | | 63) ReleaseMode = 0
| | | 64) SequenceNumber = 0
| | | 65) Anti-Blur = 2

| | | 66) Sony_0xb04c = 1 (10/10)
| | | 67) Sony_0xb04d = 0
| | | 68) DynamicRangeOptimizer = 1
| | | 69) Sony_0xb050 = 65535
| | | 70) Sony_0xb051 = 0
| | | 71) IntelligentAuto = 0
| | | 72) Sony_0xb053 = 0
| | | 73) WhiteBalance = 0
| | | 74) Tag9400c (SubDirectory) -->
| | | + [BinaryData directory, 556 bytes]

| | | | ReleaseMode2 = 0
| | | | ShotNumberSincePowerUp = 2
| | | | SequenceImageNumber = 0
| | | | SequenceLength = 1
| | | | SequenceFileNumber = 0
| | | | SequenceLength = 1
| | | | CameraOrientation = 1
| | | | Quality2 = 0
| | | | SonyImageHeight = 500
| | | | ModelReleaseYear = 14

| | | 75) Sony_0x9401 = ..f....pW..).6Q.\.(.G.G...6\6.G.G...6\6..$..........^...3..s.[snip]
| | | 76) Tag9402 (SubDirectory) -->
| | | + [BinaryData directory, 400 bytes]
| | | | AmbientTemperature = 21
| | | | FocusMode = 2
| | | | AFAreaMode = 1
| | | | FocusPosition2 = 202
| | | 77) Tag9403 (SubDirectory) -->
| | | + [BinaryData directory, 1000 bytes]
| | | 78) Tag9404b (SubDirectory) -->

| | | + [BinaryData directory, 556 bytes]
| | | | LensZoomPosition = 27
| | | 79) Tag9405b (SubDirectory) -->
| | | + [BinaryData directory, 1992 bytes]
| | | | SonyISO = 4096
| | | | BaseISO = 4096
| | | | StopsAboveBaseISO = 4096
| | | | SonyExposureTime2 = 6208
| | | | ExposureTime = 0.003125
| | | | SonyFNumber = 5888

| | | | SonyMaxApertureValue = 5054
| | | | SequenceImageNumber = 0
| | | | ReleaseMode2 = 0
| | | | HighISONoiseReduction = 2
| | | | LongExposureNoiseReduction = 1
| | | | PictureEffect2 = 0
| | | | ExposureProgram = 1
| | | | CreativeStyle = 0
| | | | Sharpness = 0
| | | | DistortionCorrection = 0

| | | | LensFormat = 1
| | | | LensMount = 2
| | | | LensType2 = 32787
| | | | LensZoomPosition = 27
| | | 80) Tag9406 (SubDirectory) -->
| | | + [BinaryData directory, 64 bytes]
| | | | BatteryTemperature = 87
| | | | BatteryLevel = 55
| | | 81) Sony_0x9407 = .2WW.^....AT..W..>.WSi...lw...W.............T..i.......T....W.l.
| | | 82) Sony_0x9408 = i....*......\...*..}......u}..C.=..@.[.8k.&...|......C^..pp..[snip]

| | | 83) Sony_0x9409 = . ...l.(....I.........`...s..........i7.@.Sp......>........@..........z...%...>.[snip]
| | | 86) Tag940c (SubDirectory) -->
| | | + [BinaryData directory, 64 bytes]
| | | | LensMount2 = 4
| | | | LensType3 = 32787
| | | | CameraE-mountVersion = 336
| | | | LensE-mountVersion = 263
| | | | LensFirmwareVersion = 1
| | | 87) Sony_0x940d = ......g......z...?..u.............GO.D.....qUx...?....el....8[snip]
| | | 88) Tag940e (SubDirectory) -->

| | | + [BinaryData directory, 12288 bytes]
| | | 89) Sony_0xa100 =
| | | 90) Tag2010g (SubDirectory) -->
| | | + [BinaryData directory, 6556 bytes]
| | | | ReleaseMode2 = 0
| | | | DynamicRangeOptimizer = 1
| | | | ReleaseMode3 = 0
| | | | ReleaseMode2 = 0
| | | | FlashMode = 1
| | | | StopsAboveBaseISO = 4096

| | | | BrightnessValue = 17245
| | | | DynamicRangeOptimizer = 1
| | | | HDRSetting = 0
| | | | ExposureCompensation = 0
| | | | PictureProfile = 0
| | | | PictureProfile = 0
| | | | PictureEffect2 = 0
| | | | Quality2 = 0
| | | | MeteringMode = 0
| | | | ExposureProgram = 1

| | | | WB_RGBLevels = 690 256 427
| | | | FocalLength = 180
| | | | MinFocalLength = 180
| | | | MaxFocalLength = 2000
| | | | SonyISO = 4096
| | | | LensFormat = 1
| | | | LensMount = 2
| | | | LensType2 = 32787
| | | 91) Sony_0x940f = liVi.$........)....@..f.........&....@...Y@)..Y@.@...........[snip]
| | | 92) Tag9050 (SubDirectory) -->

| | | + [BinaryData directory, 944 bytes]
| | | | FlashStatus = 64
| | | | ImageCount = 3836
| | | | SonyFNumber = 5888
| | | | ReleaseMode2 = 0
| | | | ImageCount2 = 3836
| | | | SonyDateTime2 = ....#.
| | | | ReleaseMode2 = 0
| | | | InternalSerialNumber = 162 254 226 13
| | | | LensMount = 2

| | | | LensFormat = 1
| | | | LensType2 = 32787
| | | | LensSpecFeatures = .
| | | | ImageCount3 = 44765
| | | 93) Sony_0x9412 = ...@.V.......................................................[snip]
| | 19) UserComment =
| | 20) FlashpixVersion = 0100
| | 21) ColorSpace = 1
| | 22) ExifImageWidth = 6000
| | 23) ExifImageHeight = 4000

| | 24) InteropOffset (SubDirectory) -->
| | + [InteropIFD directory with 2 entries]
| | | 0) InteropIndex = R98
| | | 1) InteropVersion = 0100
| | 25) FileSource = 3
| | 26) SceneType = 1
| | 27) CustomRendered = 0
| | 28) ExposureMode = 0
| | 29) WhiteBalance = 0
| | 30) DigitalZoomRatio = 1 (16/16)

| | 31) FocalLengthIn35mmFormat = 27
| | 32) SceneCaptureType = 0
| | 33) Contrast = 0
| | 34) Saturation = 0
| | 35) Sharpness = 0
| | 36) LensInfo = 18 200 3.5 6.3 (180/10 2000/10 35/10 63/10)
| | 37) LensModel = E 18-200mm F3.5-6.3 OSS
| 12) PrintIM (SubDirectory) -->
| + [PrintIM directory with 3 entries]
| | PrintIMVersion = 0300

| | 0) PrintIM_0x0002 = 1
| | 1) PrintIM_0x0003 = 34
| | 2) PrintIM_0x0101 = 0
+ [IFD1 directory with 13 entries]
| 0) Compression = 6
| 1) ImageDescription =
| 2) Make = SONY
| 3) Model = ILCE-6000
| 4) Orientation = 1
| 5) XResolution = 72 (72/1)

| 6) YResolution = 72 (72/1)
| 7) ResolutionUnit = 2
| 8) Software = ILCE-6000 v3.20
| 9) ModifyDate = 2018:05:11 08:35:11
| 10) ThumbnailOffset = 38476
| 11) ThumbnailLength = 6426
| 12) YCbCrPositioning = 2
JPEG APP1 (2825 bytes):
+ [XMP directory, 2796 bytes]
| XMPToolkit = XMP Core 4.4.0-Exiv2

| Software = digiKam-5.6.0
| CreatorTool = digiKam-5.6.0
| Caption = Grand Canyon-Parashant National Monument, along the Clayhole Road
| Title = Grand Canyon-Parashant National Monument, along the Clayhole Road
JPEG APP13 (126 bytes):
+ [Photoshop directory, 112 bytes]
| IPTCData (SubDirectory) -->
| + [IPTC directory, 99 bytes]
| | CurrentIPTCDigest = ~.g..z....3P}R..
| | -- IPTCEnvelope record --

| | CodedCharacterSet = .%G
| | -- IPTCApplication record --
| | OriginatingProgram = digiKam
| | ProgramVersion = 5.6.0
| | ObjectName = Grand Canyon-Parashant National Monument, along the Clayhole Roa
JPEG APP2 (156 bytes):
+ [MPF0 directory with 3 entries]
| 0) MPFVersion = 0100
| 1) NumberOfImages = 2
| 2) MPImageList (SubDirectory) -->

| + [BinaryData directory, 16 bytes]
| | MPImageFlags = 2684354560
| | MPImageFormat = 0
| | MPImageType = 196608
| | MPImageLength = 8768466
| | MPImageStart = 0
| | DependentImage1EntryNumber = 2
| | DependentImage2EntryNumber = 0
| + [BinaryData directory, 16 bytes]
| | MPImageFlags = 1073741824

| | MPImageFormat = 0
| | MPImageType = 65538
| | MPImageLength = 664145
| | MPImageStart = 8724632
| | DependentImage1EntryNumber = 0
| | DependentImage2EntryNumber = 0
JPEG DQT (130 bytes):
JPEG DHT (416 bytes):
JPEG SOF0 (15 bytes):
ImageWidth = 6000

ImageHeight = 4000
EncodingProcess = 0
BitsPerSample = 8
ColorComponents = 3
JPEG SOS
Warning = [minor] Error reading PreviewImage
PreviewImage = SCALAR(0x19b5ab8)

What tools exist to automatically digitally remove sensor dust from a large series of photos? I have hundreds of affected photos (essentially anything taken at F/8 or smaller). It is most obvious when the affected region is in the sky, so a solution that works for the sky only is satisfactory. Assuming that the dust doesn't move too far, it should be possible to use some kind of matching technique to calculate a new mask for each photo, before applying it using inpainting or some other method. Although simple in theory, implementing this may be quite laborious in practice — and I'm not even sure if inpainting is a good solution.


Short of manually repairing each photo (it takes 4 minutes of calculation time alone on my old desktop), what tools exist to repair my entire series of photos (or at least those taken at F/8 or smaller with sky in the affected areas)?



I work on Linux and my usual toolkit consists of digikam, darktable, gimp, and Python. The camera is a fairly new Sony A6000 (mirrorless / compact system camera). Some photos are taken with a Sony 18–200mm OSS lens, others with a Samyang 12 mm manual lens.



Answer



Something like this currently has no technical solution - I guess you need to do it manually or wait for an AI/ML backed solution.


With that being said, you might achieve some success with this tool:


http://www.batchcrop.com/dust-spot-removal.php


It costs 49$.


No comments:

Post a Comment

Why is the front element of a telephoto lens larger than a wide angle lens?

A wide angle lens has a wide angle of view, therefore it would make sense that the front of the lens would also be wide. A telephoto lens ha...