Removing ghost rings on CCD reading?

51 views (last 30 days)
Timothy
Timothy on 8 Jan 2026 at 2:58
Answered: Image Analyst on 11 Jan 2026 at 16:25
Hello! I have a matrix were each value contains a level of intensity. I do some data manipulation on my data, and create these plots. My function that removes the ghost rings is horrible because it pouches everything. My goal is to only pouch the shadow ring. Can I get some advice on how to deal with it? TThanks!
  5 Comments
Mathieu NOE
Mathieu NOE on 8 Jan 2026 at 7:55
if I look at the second image this seems quite a nice result - what is the "other stuff" that we shall not impact ?
Timothy
Timothy on 8 Jan 2026 at 8:37
The fuzzy looking stuff should stay. The ONLY issue is the fait shifted curve inside of that fuzzy area.

Sign in to comment.

Answers (3)

Image Analyst
Image Analyst on 8 Jan 2026 at 21:58
To me, in the US, poach is something you do to cook an egg, or to hunt animals illegally. I assume you mean "remove" or "filter out". How many do you have to do? Could you just manually paint them out. If you have hundred of them, you might make a script. If the inside of the ring is supposed to be black, I would first threshold the image at a high enough level to only get the major ring. Then use imdilate to widen the ring a desired amount. Then use the mask to erase everything outside of the ring. Something like (untested)
mask = grayImage > someGrayLevel; % You specify this, or try using imbinarize().
se = strel('disk', 7); % Change 7 to whatever width you want.
mask = imdilate(mask, se); % Widen the mask.
% Erase gray image outside the mask
outputImage = grayImage; % Initialize
outputImage(~mask) = 0; % Erase.
imshow(outputImage, []);
If you have any more questions, then attach your image with the paperclip icon after you read this:

Mathieu NOE
Mathieu NOE on 9 Jan 2026 at 8:38
made a quick and dirty code just to see how far I could go without the Image procesing Tbx
well, you may find that usefull or not maybe with some extra work you can further improve it to the performance level you want .
the code requires you to download this fex submission : inpaint_nans - File Exchange - MATLAB Central
% Read the input image
A = imread('image0.png');
A = rgb2gray(A); % Convert to grayscale if it's a color image
A = double(A);
A = flipud(A); % to have image displayed with correct y direction
[yy,xx] = size(A);
[y,x] = find(A>60); % threshold (identify main ring)
figure(1)
imagesc(A);
colorbar('vert');
set(gca,'YDir','normal');
colormap('gray');
hold on
axis square
plot(x,y,'.')
% center of main ring
xc = mean(x);
yc = mean(y);
[theta,r] = cart2pol(x-xc,y-yc);
%% find the inner boundary of the main ring
% "interpolated/mean" of r,z values : take the average of r and z within an angle domain
Npoints = 180;
theta_new = linspace(min(theta),max(theta),Npoints);
dt = mean(diff(theta_new));
for k = 1:Npoints
ind = (theta>=theta_new(k)-dt/2) & (theta<theta_new(k)+dt/2);
if isempty(ind)
r_new(k) = NaN;
else
r_new(k) = min(r(ind));
end
end
% remove outliers inside main ring (bright spots)
ind = r_new./mean(r_new)<0.8;
r_new(ind) = [];
theta_new(ind) = [];
% convert to cartesian
[xn,yn] = pol2cart(theta_new,r_new);
% add back centroid info
xn = xn + xc;
yn = yn + yc;
% closing the curve
xn(end+1) = xn(1);
yn(end+1) = yn(1);
plot(xn,yn,'g.')
%% select pixels inside main ring (using inpolygon)
[X,Y] = meshgrid((1:xx),(1:yy));
in = inpolygon(X(:),Y(:),xn,yn);
logical_mask = reshape(in,size(A));
figure(2)
B = A.*logical_mask;
imagesc(B);
colorbar('vert');
set(gca,'YDir','normal');
colormap('gray');
clim([0 100])
axis square
figure(3)
B(B>27) = NaN; % remove major ghost circle (quite sensitive to threshold)
% now fill / interpolate NaN values
Bf = inpaint_nans(B,0);
imagesc(B);
colorbar('vert');
set(gca,'YDir','normal');
colormap('gray');
axis square
clim([0 100])
AA = A.*(~logical_mask) + Bf;
figure(4)
imagesc(AA);
colorbar('vert');
set(gca,'YDir','normal');
colormap('gray');
axis square

Image Analyst
Image Analyst on 11 Jan 2026 at 16:25
Try this (requires Image Processing Toolbox, which you probably have). It takes away the ghost ring but leaves the briht specks that you said you want. If you also want the dark background inside the ring (and don't want it set to 0) then we'll have to threshold to get just the ghost ring, but it will be tricky since it's faint and you want to keep the faint gray levels next to the bright ring. If that's the case let me know and I'll have to make the script more sophisticated.
% Demo by Image Analyst
% Initialization steps:
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables. Or clearvars if you want.
format long g;
format compact;
fontSize = 18;
%--------------------------------------------------------------------------------------------------------
% READ IN FIRST IMAGE
folder = pwd;
baseFileName = "image0.png";
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~isfile(fullFileName)
% The file doesn't exist -- didn't find it there in that folder.
% Check the entire search path (other folders) for the file by stripping off the folder.
fullFileNameOnSearchPath = baseFileName; % No path this time.
if ~exist(fullFileNameOnSearchPath, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist in the search path folders.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
% Read in image file.
grayImage = imread(fullFileName);
% Get size
[rows, columns, numberOfColorChannels] = size(grayImage)
rows = 310
columns = 313
numberOfColorChannels = 3
% Get gray scale version of it.
if numberOfColorChannels == 3
grayImage = grayImage(:, :, 1); % Take red channel.
end
% Display the image.
subplot(2, 2, 1);
% Use imadjust just to expand the dynamic range of the display
% but it's not changing the grayImage pixel values in the variable.
imshow(imadjust(grayImage), []);
axis('on', 'image');
impixelinfo;
caption = sprintf('Original image : %s', baseFileName);
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
% Maximize window.
g = gcf;
g.WindowState = 'maximized';
g.Name = 'Demo by Image Analyst';
g.NumberTitle = 'off';
drawnow;
%--------------------------------------------------------------------------------------------------------
% Display the histogram of the image.
subplot(2, 2, 2);
imhist(grayImage);
grid on;
title('Histogram of Image', 'FontSize', fontSize, 'Interpreter', 'None');
%--------------------------------------------------------------------------------------------------------
% Set initial threshold limits
lowThreshold = 40;
highThreshold = 255;
% Interactively and visually set a threshold on a gray scale image.
% https://www.mathworks.com/matlabcentral/fileexchange/29372-thresholding-an-image?s_tid=srchtitle
% [lowThreshold, highThreshold] = threshold(40, 255, grayImage);
% Threshold the image to create a mask. Threshold should be high
% enough such that it does not include any of the ghost ring.
mask = grayImage >= lowThreshold & grayImage <= highThreshold;
%--------------------------------------------------------------------------------------------------------
% Widen the mask.
se = strel('disk', 7); % Change 7 to whatever width you want.
mask = imdilate(mask, se); % Widen the mask.
% Display the mask image.
subplot(2, 2, 3);
imshow(mask);
axis('on', 'image');
impixelinfo;
title('Mask', 'FontSize', fontSize, 'Interpreter', 'None');
% Erase gray image outside the mask
outputImage = grayImage; % Initialize
outputImage(~mask) = 0; % Erase.
% Display the final gray scale image.
subplot(2, 2, 4);
imshow(imadjust(outputImage), []);
axis('on', 'image');
impixelinfo;
title('Final Image', 'FontSize', fontSize, 'Interpreter', 'None');

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!