8

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.

  • 1
    Oh man — another job for ML! – mattdm Jun 12 '18 at 22:42
  • 1
    @mattdm ML = Machine Learning? I suppose it is! – gerrit Jun 12 '18 at 23:18
  • 3
    Yeah. I'm thinking it's time to start a betting pool for when this site will merge into ai.stackexchange.com :) – mattdm Jun 12 '18 at 23:31
  • @xiota I've edited into the question: Sony A6000; I'm fairly certain it's sensor dust because it accumulated during the vacation, was visible with a magnifying glass, and disappeared after professional sensor cleaning. I suppose it's the dust moving relative to the sensor, but I haven't heard of IBIS before. Some transparent plate in front of the sensor that may shift by 50–100 µm? – gerrit Jun 13 '18 at 0:20
  • IBIS = in-body image stabilization. Here is a video of one in action: YouTube: Minolta Anti-Shake. Moves a lot more than I thought. – xiota Jun 13 '18 at 0:30
3

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$.

  • 2
    I ended up doing it manually (with darktable), it took a long time but I got a good and detailed look at many vacation photos! – gerrit Nov 7 '18 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.