I'm new to image processing and started using MATLAB for Astrophotography processing. I'm trying to process 10 corrupted images (same image but mixed with different noise) of the planet Saturn using MATLAB. I learned that by stacking the 10 images together leads to a noise reduced picture with high PSNR and tried the below coding to make it work.
But the output looks like an unclear saturated image with no noise reduction.
Can you please look at the code below and show me where I went wrong?
%% We are going to stack the 10 corrupted images and finally calculate the PSNR SSIM
clearvars;% Clear all the variables
close all;
load('planetdata.mat'); %to load the corrupted Image set (4-D uint8)
Clean = imread('Clean Image of Saturn.jpg');%Clean Image of Saturn.600x800x3 uint8
planet1(: , :, :) = planetdata(1, :, :, :);%One corrupted Image as reff
% Set the number of images to stack is 10
stack_number = 10;
% Lets use Clean image as reference of dimensions required
im_x = size(Clean, 1);
im_y = size(Clean, 2);
im_z = size(Clean, 3);
% Lets Generate a blank image for image stacking
resultIM = uint8(zeros(im_x, im_y, im_z));
% Iterate through the images to stack
for i = 1:1:stack_number
% Read in the target object image
CorruptIM(: , :, :) = planetdata(i, :, :, :);
% Perform image stacking using the target object image
resultIM = resultIM + CorruptIM;
end
% resultIM = resultIM / stack_number;
%% Lets Display Results
workspace; % to Make sure the work space panel is showing.
fontSize = 15;
figure;
subplot(1, 3, 1);
imshow(Clean);
title('Clean Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'Position', get(0,'Screensize'));
% Give a name to the title bar.
set(gcf,'name','Stacking','numbertitle','off')
% Display one corrupt image as reference
subplot(1, 3, 2);
imshow(planet1);
title('Corrupt Image 1 : Ref', 'FontSize', fontSize);
% Display Stacked image
subplot(1, 3, 3);
imshow(resultIM);
title('Stacked Image', 'FontSize', fontSize);
%% PSNR AND SSIM Calculation
%Lets Find PSNR for For Resultant Image
[row,col] = size(Clean);
size_host = row*col;
o_double = double(Clean);
w_double = double(resultIM);
s=0;
for j = 1:size_host % the size of the original image
s = s+(w_double(j) - o_double(j))^2 ;
end
mes =s/size_host;
psnr =10*log10((255)^2/mes);
fprintf('The PSNR value for Stacked Image is %0.4f.\n',psnr);
%Lets Find SSIM for resultant Image
[ssimval, ssimmap] = ssim(uint8(resultIM),Clean);
fprintf('The SSIM value for Stacked Image is %0.4f.\n',ssimval);
I think this one statement pretty much says it all (emphasis mine):
But the output looks like an unclear saturated image with no noise reduction.
It looks like your images are in fact saturating at the upper limit for a uint8 variable, which is the data type for your result image resultIM and your data matrix planetdata. As you keep adding images, the pixel values saturate at the maximum value of 255 for unsigned 8-bit integer types.
What you'll need to do is convert to a larger data type for your intermediate calculations, such as a larger integer type or a floating point type. Then, once your calculations are complete (e.g. dividing the sum by the image stack size to get an average image), you can scale and/or round your data as needed and convert it back to a uint8 type.
Related
I'm trying to obtain an image which has everything but several colorful objects grayed out, as shown here:
My original image is this (the caps have slightly different colors than the example above):
I tried to apply a threshold process then binarize the image, which gave me the following result (mask is on the left, result of multiplication is on the right):
And now I'm trying to combine all of these masks. Should I use if loop to combine it into a single image or is there a better way? I tried using (&,&,&) but it turned into a black images.
Your original image has 7 distinct regions: 5 colorful tips, the hand, and the background. The question then becomes, how do we disregard the wall and the hand, which happen to be the two largest regions, and only keep the color of the tips.
If your MATLAB license permits, I would recommend using the Color Thresholder App (colorThresholder), which allows you to find a suitable representation of the colors in your image. Experimenting with it for a minute I can say that the L*a*b* color space allows good separation between the regions/colors:
We can then export this function, yielding the following:
function [BW,maskedRGBImage] = createMask(RGB)
%createMask Threshold RGB image using auto-generated code from colorThresholder app.
% [BW,MASKEDRGBIMAGE] = createMask(RGB) thresholds image RGB using
% auto-generated code from the colorThresholder app. The colorspace and
% range for each channel of the colorspace were set within the app. The
% segmentation mask is returned in BW, and a composite of the mask and
% original RGB images is returned in maskedRGBImage.
% Auto-generated by colorThresholder app on 25-Dec-2018
%------------------------------------------------------
% Convert RGB image to chosen color space
I = rgb2lab(RGB);
% Define thresholds for channel 1 based on histogram settings
channel1Min = 0.040;
channel1Max = 88.466;
% Define thresholds for channel 2 based on histogram settings
channel2Min = -4.428;
channel2Max = 26.417;
% Define thresholds for channel 3 based on histogram settings
channel3Min = -12.019;
channel3Max = 38.908;
% Create mask based on chosen histogram thresholds
sliderBW = (I(:,:,1) >= channel1Min ) & (I(:,:,1) <= channel1Max) & ...
(I(:,:,2) >= channel2Min ) & (I(:,:,2) <= channel2Max) & ...
(I(:,:,3) >= channel3Min ) & (I(:,:,3) <= channel3Max);
BW = sliderBW;
% Invert mask
BW = ~BW;
% Initialize output masked image based on input image.
maskedRGBImage = RGB;
% Set background pixels where BW is false to zero.
maskedRGBImage(repmat(~BW,[1 1 3])) = 0;
end
Now that you have the mask we can easily convert the original image to grayscale, replicate it along the 3rd dimension, then take the colorful pixels from the original image using logical indexing:
function q53922067
img = imread("https://i.stack.imgur.com/39WNm.jpg");
% Image segmentation:
BW = repmat( createMask(img), 1, 1, 3 ); % Note that this is the function shown above
% Keeping the ROI colorful and the rest gray:
gImg = repmat( rgb2gray(img), 1, 1, 3 ); % This is done for easier assignment later
gImg(BW) = img(BW);
% Final result:
figure(); imshow(gImg);
end
Which yields:
To combine masks, use | (element-wise logical OR), not & (logical AND).
mask = mask1 | mask2 | mask3;
I have an image (white background with 1-5 black dots) that is called main.jpg (main image).
I am trying to place another image (secondary.jpg) in every black dot that is found in main image.
In order to do that:
I found the black pixels in main image
resize the secondary image to specific size that I want
plot the image in every coordinate that I found in step one. (the black pixel should be the center coordinates of the secondary image)
Unfortunately, I don't know how to do the third step.
for example:
main image is:
secondary image is:
output:
(The dots are behind the chairs. They are the image center points)
This is my code:
mainImage=imread('main.jpg')
secondaryImage=imread('secondary.jpg')
secondaryImageResized = resizeImage(secondaryImage)
[m n]=size(mainImage)
for i=1:n
for j=1:m
% if it's black pixel
if (mainImage(i,j)==1)
outputImage = plotImageInCoordinates(secondaryImageResized, i, j)
% save this image
imwrite(outputImage,map,'clown.bmp')
end
end
end
% resize the image to (250,350) width, height
function [ Image ] = resizeImage(img)
image = imresize(img, [250 350]);
end
function [outputImage] = plotImageInCoordinates(image, x, y)
% Do something
end
Any help appreciated!
Here's an alternative without convolution. One intricacy that you must take into account is that if you want to place each image at the centre of each dot, you must determine where the top left corner is and index into your output image so that you draw the desired object from the top left corner to the bottom right corner. You can do this by taking each black dot location and subtracting by half the width horizontally and half the height vertically.
Now onto your actual problem. It's much more efficient if you loop through the set of points that are black, not the entire image. You can do this by using the find command to determine the row and column locations that are 0. Once you do this, loop through each pair of row and column coordinates, do the subtraction of the coordinates and then place it on the output image.
I will impose an additional requirement where the objects may overlap. To accommodate for this, I will accumulate pixels, then find the average of the non-zero locations.
Your code modified to accommodate for this is as follows. Take note that because you are using JPEG compression, you will have compression artifacts so regions that are 0 may not necessarily be 0. I will threshold with an intensity of 128 to ensure that zero regions are actually zero. You will also have the situation where objects may go outside the boundaries of the image. Therefore to accommodate for this, pad the image sufficiently with twice of half the width horizontally and twice of half the height vertically then crop it after you're done placing the objects.
mainImage=imread('https://i.stack.imgur.com/gbhWJ.png');
secondaryImage=imread('https://i.stack.imgur.com/P0meM.png');
secondaryImageResized = imresize(secondaryImage, [250 300]);
% Find half height and width
rows = size(secondaryImageResized, 1);
cols = size(secondaryImageResized, 2);
halfHeight = floor(rows / 2);
halfWidth = floor(cols / 2);
% Create a padded image that contains our main image. Pad with white
% pixels.
rowsMain = size(mainImage, 1);
colsMain = size(mainImage, 2);
outputImage = 255*ones([2*halfHeight + rowsMain, 2*halfWidth + colsMain, size(mainImage, 3)], class(mainImage));
outputImage(halfHeight + 1 : halfHeight + rowsMain, ...
halfWidth + 1 : halfWidth + colsMain, :) = mainImage;
% Find a mask of the black pixels
mask = outputImage(:,:,1) < 128;
% Obtain black pixel locations
[row, col] = find(mask);
% Reset the output image so that they're all zeros now. We use this
% to output our final image. Also cast to ensure accumulation is proper.
outputImage(:) = 0;
outputImage = double(outputImage);
% Keeps track of how many times each pixel was hit by the object
% This is so that we can find the average at each location.
counts = zeros([size(mask), size(mainImage, 3)]);
% For each row and column location in the image
for i = 1 : numel(row)
% Get the row and column locations
r = row(i); c = col(i);
% Offset to get the top left corner
r = r - halfHeight;
c = c - halfWidth;
% Place onto final image
outputImage(r:r+rows-1, c:c+cols-1, :) = outputImage(r:r+rows-1, c:c+cols-1, :) + double(secondaryImageResized);
% Accumulate the counts
counts(r:r+rows-1,c:c+cols-1,:) = counts(r:r+rows-1,c:c+cols-1,:) + 1;
end
% Find average - Any values that were not hit, change to white
outputImage = outputImage ./ counts;
outputImage(counts == 0) = 255;
outputImage = uint8(outputImage);
% Now crop and show
outputImage = outputImage(halfHeight + 1 : halfHeight + rowsMain, ...
halfWidth + 1 : halfWidth + colsMain, :);
close all; imshow(outputImage);
% Write the final output
imwrite(outputImage, 'finalimage.jpg', 'Quality', 100);
We get:
Edit
I wasn't told that your images had transparency. Therefore what you need to do is use imread but ensure that you read in the alpha channel. We then check to see if one exists and if one does, we will ensure that the background of any values with no transparency are set to white. You can do that with the following code. Ensure this gets placed at the very top of your code, replacing the images being loaded in:
mainImage=imread('https://i.stack.imgur.com/gbhWJ.png');
% Change - to accommodate for transparency
[secondaryImage, ~, alpha] = imread('https://i.imgur.com/qYJSzEZ.png');
if ~isempty(alpha)
m = alpha == 0;
for i = 1 : size(secondaryImage,3)
m2 = secondaryImage(:,:,i);
m2(m) = 255;
secondaryImage(:,:,i) = m2;
end
end
secondaryImageResized = imresize(secondaryImage, [250 300]);
% Rest of your code follows...
% ...
The code above has been modified to read in the basketball image. The rest of the code remains the same and we thus get:
You can use convolution to achieve the desired effect. This will place a copy of im everywhere there is a black dot in imz.
% load secondary image
im = double(imread('secondary.jpg'))/255.0;
% create some artificial image with black indicators
imz = ones(500,500,3);
imz(50,50,:) = 0;
imz(400,200,:) = 0;
imz(200,400,:) = 0;
% create output image
imout = zeros(size(imz));
imout(:,:,1) = conv2(1-imz(:,:,1),1-im(:,:,1),'same');
imout(:,:,2) = conv2(1-imz(:,:,2),1-im(:,:,2),'same');
imout(:,:,3) = conv2(1-imz(:,:,3),1-im(:,:,3),'same');
imout = 1-imout;
% output
imshow(imout);
Also, you probably want to avoid saving main.jpg as a .jpg since it results in lossy compression and will likely cause issues with any method that relies on exact pixel values. I would recommend using .png which is lossless and will also likely compress better than .jpg for synthetic images where the same colors repeat many times.
I have a specific question to ask about the intensity adjustment for image processing. I need high constraint value to find small gaps in the image which is shown as a red circle in the image. I used a manual threshold value 0.99 to convert the grayscale image to binary image for other processing methods. However, as the illumination on the surface did not distribute evenly, some parts of the image is lost. I used the adaptive method suggested by Matlab, however, the results is similar to a global threshold graythresh.
I will show my code and result below.
I0 = imread('1_2.jpg');
[R,C,K] = size(I0);
if K==1
I1 = I0;
else
I1 = rgb2gray(I0);
end
%Adjsut image to get a standar binary picture
%Adjust image intensity value
I1 = imadjust(I1,[0.1 0.7],[]);
BW0 = im2bw(I1,0.99);
figure;
BW0 = bwareaopen(BW0,10000);
%Fill non_crack hole error
BW0 = bwareaopen(1-BW0,500);
BW0 = 1-BW0;
imshow(BW0);
After this process, only half of the image will be left. I want a whole image, with locally intensity threshold but show the same feature as the high-level threshold. What can I do?
Thanks
Try adaptthresh:
I0 = imread('1_2.jpg');
[R,C,K] = size(I0);
if K==1
I1 = I0;
else
I1 = rgb2gray(I0);
end
T = adaptthresh(I1, 0.4); %adaptive thresholding
% Convert image to binary image, specifying the threshold value.
BW = imbinarize(I1,T);
% Display the original image with the binary version, side-by-side.
figure
imshowpair(I1, BW, 'montage')
I have a code that is creating and capturing a jpg file. However, it tends to come out with two different image sizes. This becomes a problem when I want to make the jpg files into a movie. I have set PaperPosition and a few other things as well, but it still comes out with two image sizes. The image size change seems random since if I run the code twice, an image that had one of the sizes before could have the other size now.
nFrames = 8797; % Number of frames. Number of days between 1/1/1990 and 1/31/2014
for k = 1:nFrames % 1/1/1990 to the number of days.
% Map of conterminous US
ax = figure(1);
set(ax, 'visible', 'off', 'units','normalized','outerposition',[0 0 1 1]); % Make window that shows up full sized, which makes saved figure clearer
ax = usamap('conus');
states = shaperead('usastatelo', 'UseGeoCoords', true,...
'Selector',...
{#(name) ~any(strcmp(name,{'Alaska','Hawaii'})), 'Name'});
faceColors = makesymbolspec('Polygon',...
{'INDEX', [1 numel(states)], 'FaceColor', 'none'}); % NOTE - colors are random
geoshow(ax, states, 'DisplayType', 'polygon', ...
'SymbolSpec', faceColors)
framem off; gridm off; mlabel off; plabel off
hold on
% Plot data
scatterm(ax,str2double(Lat_O3{k}), str2double(Lon_O3{k}), 40, str2double(data_O3{k})*1000, 'filled'); % Plot a dot at each Lat and Lon
hold on
% Colorbar
caxis([10 90]);
h = colorbar;
ylabel(h,'ppb');
% Title
% date = datenum(2007, 04, 29) + k; % Convert t into serial numbers.
title(['O3 MDA8 Concentration ', datestr(cell2mat(date_O3(k)), 'mmm dd yyyy')]); % Title changes every daytitle(str);
% Capture the frame
mov(k) = getframe(gcf); % Makes figure window pop up
% Set size of image
set(gcf,'Units','points')
set(gcf,'PaperUnits','points')
size = get(gcf,'Position');
size = size(3:4);
set(gcf,'PaperSize',size)
set(gcf,'PaperPosition',[0,0,size(1),size(2)])
% Save as jpg (just specify other format as necessary) - Must set 'facecolor' to 'none' or else color of states turn out black
eval(['print -djpeg map_US_' datestr(cell2mat(date_O3(k)),'yyyy_mm_dd') '_O3_MDA8.jpg']);
% saveas(gca, ['WI_' datestr(cell2mat(Date)) '_PM25.jpg']);
clf
end
% Save as AVI file - Set 'facecolor' to 'white'. It looks better.
close(gcf)
How can I set the image size (hopefully to full screen like I tried to do in set(ax, ...) so that when I save the jpg files, they are all the same size?
You can simply resize the image to whatever size you want, so you know it will be constant.
B = imresize(A, [numrows numcols])
Look at the imresize documentation if you have other particular questions about it.
I have to process a very large image ( say 10 MB image file or even more).I have to remove artifacts and dead pixels in MATLAB
I have read about Block Processing of Large Images, but have no idea how to apply it to a 16 bit image.
I am referring to removal of pixels which have highest value into the average value of surrounding pixel .my code is not working on my image which is 80 MB of size
numIterations = 30;
avgPrecisionSize = 16; % smaller is better, but takes longer
% Read in the image grayscale:
originalImage = double(rgb2gray(imread('C:\Documents and Settings\admin\Desktop\TM\image5.tif')));
% get the bad pixels where = 0 and dilate to make sure they get everything:
badPixels = (originalImage == 0);
badPixels = imdilate(badPixels, ones(12));
%# Create a big gaussian and an averaging kernel to use:
G = fspecial('gaussian',[1 1]*100,50);
H = fspecial('average', [1,1]*avgPrecisionSize);
%# User a big filter to get started:
newImage = imfilter(originalImage,G,'same');
newImage(~badPixels) = originalImage(~badPixels);
% Now average to
for count = 1:numIterations
newImage = imfilter(newImage, H, 'same');
newImage(~badPixels) = originalImage(~badPixels);
end
%% Plot the results
figure(123);
clf;
% Display the mask:
subplot(1,2,1);
imagesc(badPixels);
axis image
title('Region Of the Bad Pixels');
% Display the result:
subplot(1,2,2);
imagesc(newImage);
axis image
set(gca,'clim', [0 255])
title('Infilled Image');
colormap gray
newImage2 = roifill(originalImage, badPixels);
figure(44);
clf;
imagesc(newImage2);
colormap gray
You are doing a few things which are obvious problems (but it might depend specifically on how far you can get into the code before you run out of memory)
1) You are immediately converting the whole image to double
2) You are identifying certain pixels which you want to replace, but passing the whole image to imfilter and then throwing away (presumably) most of the output:
newImage = imfilter(originalImage,G,'same'); % filter across the entire image
newImage(~badPixels) = originalImage(~badPixels); % replace all the good pixels!
Without converting to double, why not first check where the bad pixels are, do your processing on subregions of the appropriate size around those pixels (the subregions can be converted to double and back), and then reassemble the image at the end?
blockproc may work if you can write your filtering option as a function which takes in an image area and returns the correct area - you'll have to use the border_size option appropriately and make sure your function just returns the original image without bothering to do any filtering if it hits a block with no bad pixels in. You can even have it write out to file as well.