Add push button to GUI to be able to change image for color thresholding in image processing and save button to save images

23 views (last 30 days)
S.
S. on 7 Mar 2022
Edited: S. on 25 Mar 2022
I am doing an image processing project for myself to seperate fruits from the background by using a range of the hue value and the saturation value. However, I want to add a push button to the GUI, to be able to interactively change the image and process multiple images for color thresholding. I have a main script and a function called myGUI().
At this moment, in the main script, I am reading the image, change the image dimensions, shift the color model to hsv and pass the hue value, saturation value and the resized image to the function myGUI().
In the function myGUI(), four sliders are added to find the ranges for the hue and saturation in order to get only the fruit and a push button to change the image. However, the newly uploaded image should undergo the preprocessing steps (new image dimensions, shift the color model to hsv and pass the hue value, saturation value and get the resized image, which is in the main script) and be passed to the function myGUI(). At this moment I get the following error after uploading the new image (succesfully, however without the preprocessing steps propably) and clicking on one of the slider bars:
Invalid or deleted object.
Error in myGUI>Slider (line 153)
set(h.h_im, 'CData', maskedRGBImage);
Error while evaluating UIControl Callback.
Moreover, I want to add a save button to be able to save both images as a .jpg, besides saving some important parameters.
%Main script
addpath('Measurements 15 november')
RGB_Image = imread('42.jpg');
[rows, columns, numberOfColorChannels] = size(RGB_Image);
scale = 1/2;
RGB_Image_Re = imresize(RGB_Image,scale);
HSV_B = rgb2hsv(RGB_Image_Re);
hImage = HSV_B(:, :, 1)*360;
sImage = HSV_B(:, :, 2);
vImage = HSV_B(:, :, 3);
myGUI(hImage, sImage, RGB_Image_Re);
function [] = myGUI(hImage, sImage, RGB_Image_Re)
h=struct; %Create structure to put all handles and data in
h.hImage = hImage;
h.sImage = sImage;
h.RGB_Image_Re = RGB_Image_Re;
h.f = figure(1);
set(gcf, 'Position',[450, 150, 800, 550]); %[450, 300, 800, 400])
h.ax = axes('Parent',h.f,'units','pixels','position',[120 290 290 215]);
h.h_im = imshow(h.RGB_Image_Re,'Parent',h.ax);
title(h.ax,'Initial color seperation: fruit');
h.ax2 = axes('Parent',h.f,'units','pixels','position',[450 290 290 215]);
h.h_im2 = imshow(h.RGB_Image_Re,'Parent',h.ax2);
title(h.ax2,'Initial color seperation: leaves, sky and logs');
%% Hue
%Initial values
Hue_min = 0;
Hue_max = 360;
% Slider for the lower limit of hue
h.min_Slider = uicontrol('Parent',h.f,'Style','slider', ... % Create user interface control
'Units','Pixels','Position',[221,260,419,23],...
'value', Hue_min, 'min',0, 'max',360, ...
'Callback', @Slider);
%Slider for the upper limit of hue
h.max_Slider = uicontrol('Parent',h.f,'Style','slider',... % Create user interface control
'Units','Pixels','Position',[221,195,419,23],...
'value', Hue_max, 'min',0, 'max',360,...
'Callback', @Slider);
%% Saturation
%Initial values
Sat_min = 0;
Sat_max = 1;
%Slider for the lower limit of hue
h.min_Slider2 = uicontrol('Parent',h.f,'Style','slider', ... % Create user interface control
'Units','Pixels','Position',[221,135,419,23],...
'value', Sat_min, 'min',0, 'max',1,...
'Callback', @Slider);
%Slider for the upper limit of hue
h.max_Slider2 = uicontrol('Parent',h.f,'Style','slider',... % Create user interface control
'Units','Pixels','Position',[221,75,419,23],...
'value', Sat_max, 'min',0, 'max',1,...
'Callback', @Slider);
% Label upper limit of slider
bu1 = uicontrol('Parent',h.f,'Style','text','Position',[190,75,23,23],...
'String','0');
bu2 = uicontrol('Parent',h.f,'Style','text','Position',[640,75,23,23],...
'String','1');
bu3 = uicontrol('Parent',h.f,'Style','text','Position',[275,45,200,23],...
'String','Upper limit saturation:');
PushButton = uicontrol(gcf,'Style', 'pushbutton', ...
'String', 'Upload picture', ...
'Position', [325,15,200,23], ...
'Callback', {@Pushbutton, h});
guidata(h.f,h); %Store all handles to the GUI
end
%% Callbacks
function Pushbutton(hObj,~,h)
% hObj is the button handle
[FileName, PathName] = uigetfile({'*.JPG'});
RGB_Image = imshow([PathName, FileName],'Parent',h.ax);
RGB_Image2 = imshow([PathName, FileName],'Parent',h.ax2);
end
function Slider(hObj,~)
h = guidata(hObj);
Hue_min = round(h.min_Slider.Value,3);
Hue_max = round(h.max_Slider.Value,3);
Sat_min = round(h.min_Slider2.Value,3);
Sat_max = round(h.max_Slider2.Value,3);
% Mask
mask = (h.hImage >= Hue_min) & (h.hImage <= Hue_max) & ...
(h.sImage >= Sat_min) & (h.sImage <= Sat_max);
% Create variable masked image (output) based on RGB image (input).
maskedRGBImage = h.RGB_Image_Re;
maskedRGBImage(repmat(~mask,[1 1 3])) = 0;
%Update image values
set(h.h_im, 'CData', maskedRGBImage);
Leaves = h.RGB_Image_Re.*cast(~mask, 'like', h.RGB_Image_Re);
%Update image values
set(h.h_im2, 'CData', Leaves);
end

Answers (3)

Voss
Voss on 8 Mar 2022
When the function Pushbutton is executed, the selected file is read and new images are created in each of the two axes, using imshow(), which deletes the old images in those axes. However, h.h_im still refers to the old image in h.ax, so when the Slider function is next executed, it tries to update h.h_im, which has been deleted and you get that error.
The solution is to have the program treat a newly uploaded image exactly the same as it treats the initial image that comes in as an input argument. I mean, it should go through all the same steps in each of those two situations (initializing from an image and loading in a new image). The best thing would probably be to make those steps into a function which is called from both places.
However, I'll just copy and paste some code from the main script (which does the preprocessing that you mentioned was likely missing in this case) and from the beginning of myGUI, which sets up the relevant GUI handles, and show what the pushbutton callback Pushbutton should look like:
function Pushbutton(hObj,~,h)
% hObj is the button handle
[FileName, PathName] = uigetfile({'*.JPG'});
if isequal(FileName,0)
return
end
RGB_Image = imread([PathName, FileName]);
% [rows, columns, numberOfColorChannels] = size(RGB_Image);
scale = 1/2;
RGB_Image_Re = imresize(RGB_Image,scale);
HSV_B = rgb2hsv(RGB_Image_Re);
hImage = HSV_B(:, :, 1)*360;
sImage = HSV_B(:, :, 2);
% vImage = HSV_B(:, :, 3);
h.hImage = hImage;
h.sImage = sImage;
h.RGB_Image_Re = RGB_Image_Re;
h.h_im = imshow(h.RGB_Image_Re,'Parent',h.ax);
h.h_im2 = imshow(h.RGB_Image_Re,'Parent',h.ax2);
guidata(h.f,h);
end
  1 Comment
S.
S. on 9 Mar 2022
Thanks for the explanation! I have made the new function to make the code shorter. Only the following is duplicated in both the function myGUI() and in the pushbutton function, but I assume, you can't do anything about it:
h.hImage = hImage;
h.sImage = sImage;
h.RGB_Image_Re = RGB_Image_Re;
Moreover, I want to add a pushbutton to save the parameters (Hue_min, Hue_max, Sat_min, Sat_max) and the images (h.h_im, h.h_im2) after tuning with the sliders. How can I implement the images (parameters I succeeded)? I have now this:
function SaveButton(hObj,~,h)
[file,path] = uiputfile('*.mat');
save(fullfile(path,file),'-struct', 'h', 'Hue_min','Hue_max','Sat_min','Sat_max','h_im','h_im2')
end
How can I add the two images that are shown with imshow() in the variables h.h_im and h.h_im2 to the .mat file or directly as a .jpg .

Sign in to comment.


yanqi liu
yanqi liu on 8 Mar 2022
clc; clear all; close all;
%Main script
% addpath('Measurements 15 november')
RGB_Image = imread('football.jpg');
[rows, columns, numberOfColorChannels] = size(RGB_Image);
scale = 1/2;
RGB_Image_Re = imresize(RGB_Image,scale);
HSV_B = rgb2hsv(RGB_Image_Re);
hImage = HSV_B(:, :, 1)*360;
sImage = HSV_B(:, :, 2);
vImage = HSV_B(:, :, 3);
myGUI(hImage, sImage, RGB_Image_Re);
function [] = myGUI(hImage, sImage, RGB_Image_Re)
h=struct; %Create structure to put all handles and data in
h.hImage = hImage;
h.sImage = sImage;
h.RGB_Image_Re = RGB_Image_Re;
h.f = figure(1);
set(gcf, 'Position',[450, 150, 800, 550]); %[450, 300, 800, 400])
h.ax = axes('Parent',h.f,'units','pixels','position',[120 290 290 215]);
h.h_im = imshow(h.RGB_Image_Re,'Parent',h.ax);
title(h.ax,'Initial color seperation: fruit');
h.ax2 = axes('Parent',h.f,'units','pixels','position',[450 290 290 215]);
h.h_im2 = imshow(h.RGB_Image_Re,'Parent',h.ax2);
title(h.ax2,'Initial color seperation: leaves, sky and logs');
%% Hue
%Initial values
Hue_min = 0;
Hue_max = 360;
% Slider for the lower limit of hue
h.min_Slider = uicontrol('Parent',h.f,'Style','slider', ... % Create user interface control
'Units','Pixels','Position',[221,260,419,23],...
'value', Hue_min, 'min',0, 'max',360, ...
'Callback', @Slider);
%Slider for the upper limit of hue
h.max_Slider = uicontrol('Parent',h.f,'Style','slider',... % Create user interface control
'Units','Pixels','Position',[221,195,419,23],...
'value', Hue_max, 'min',0, 'max',360,...
'Callback', @Slider);
%% Saturation
%Initial values
Sat_min = 0;
Sat_max = 1;
%Slider for the lower limit of hue
h.min_Slider2 = uicontrol('Parent',h.f,'Style','slider', ... % Create user interface control
'Units','Pixels','Position',[221,135,419,23],...
'value', Sat_min, 'min',0, 'max',1,...
'Callback', @Slider);
%Slider for the upper limit of hue
h.max_Slider2 = uicontrol('Parent',h.f,'Style','slider',... % Create user interface control
'Units','Pixels','Position',[221,75,419,23],...
'value', Sat_max, 'min',0, 'max',1,...
'Callback', @Slider);
% Label upper limit of slider
bu1 = uicontrol('Parent',h.f,'Style','text','Position',[190,75,23,23],...
'String','0');
bu2 = uicontrol('Parent',h.f,'Style','text','Position',[640,75,23,23],...
'String','1');
bu3 = uicontrol('Parent',h.f,'Style','text','Position',[275,45,200,23],...
'String','Upper limit saturation:');
PushButton = uicontrol(gcf,'Style', 'pushbutton', ...
'String', 'Upload picture', ...
'Position', [325,15,200,23], ...
'Callback', {@Pushbutton, h});
guidata(h.f,h); %Store all handles to the GUI
end
%% Callbacks
function Pushbutton(hObj,~,h)
% hObj is the button handle
[FileName, PathName] = uigetfile({'*.JPG'});
% RGB_Image = imshow([PathName, FileName],'Parent',h.ax);
% RGB_Image2 = imshow([PathName, FileName],'Parent',h.ax2);
RGB_Image = imread([PathName, FileName]);
scale = 1/2;
RGB_Image_Re = imresize(RGB_Image,scale);
HSV_B = rgb2hsv(RGB_Image_Re);
hImage = HSV_B(:, :, 1)*360;
sImage = HSV_B(:, :, 2);
vImage = HSV_B(:, :, 3);
h.hImage = hImage;
h.sImage = sImage;
h.RGB_Image_Re = RGB_Image_Re;
h.h_im = imshow(h.RGB_Image_Re,'Parent',h.ax);
title(h.ax,'Initial color seperation:image');
h.h_im2 = imshow(h.RGB_Image_Re,'Parent',h.ax2);
title(h.ax2,'Initial color seperation: leaves, sky and logs');
guidata(h.f,h);
end
function Slider(hObj,~)
h = guidata(hObj);
Hue_min = round(h.min_Slider.Value,3);
Hue_max = round(h.max_Slider.Value,3);
Sat_min = round(h.min_Slider2.Value,3);
Sat_max = round(h.max_Slider2.Value,3);
% Mask
mask = (h.hImage >= Hue_min) & (h.hImage <= Hue_max) & ...
(h.sImage >= Sat_min) & (h.sImage <= Sat_max);
% Create variable masked image (output) based on RGB image (input).
maskedRGBImage = h.RGB_Image_Re;
maskedRGBImage(repmat(~mask,[1 1 3])) = 0;
%Update image values
set(h.h_im, 'CData', maskedRGBImage);
Leaves = h.RGB_Image_Re.*cast(~mask, 'like', h.RGB_Image_Re);
%Update image values
set(h.h_im2, 'CData', Leaves);
end
  6 Comments
S.
S. on 10 Mar 2022
Clear! There is no shorter way of code to save the images, like saving some variables in one way like I did with the parameters (Hue_min, Hue_max, Sat_min, Sat_max)? Moreover, what does the variable fileStr do and is it necessary? Is it possible to use normalized coordinates instead of pixels, so you will get the same image on every laptop/pc screen?

Sign in to comment.


DGM
DGM on 24 Mar 2022
Try the attached. I kind of made a mess, but I slapped a save button in there that'll just save the four parameters and two images to a .mat file.
I changed the import process to use a common import function. The GUI is now called with no arguments. That way the workflow and code is consistent regardless of whether it's the first task or subsequent tasks loaded from the "upload" button in the GUI.

Community Treasure Hunt

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

Start Hunting!