how to remove unwanted colors from a Matlab RGB image frame, without using a loop [duplicate] - matlab

This question already has answers here:
How can I convert an RGB image to grayscale but keep one color?
(3 answers)
Closed 6 years ago.
I have an RGB image frame and I want to remove all pixels that are NOT a desired color.
How can do this without a for loop?
% Desired color: R 105, G 112, B 175
% I want to zero all pixels that are not this color (plus a tad).
red_target = 105;
green_target = 112;
blue_target = 175;
tad = 4;
red_low = red_target - tad;
red_hi = red_target + tad;
green_low = green_target - tad;
green_hi = green_target + tad;
blue_low = blue_target - tad;
blue_hi = blue_target + tad;
% Filter out non-target colors:
% Pixel redness is within target; greenness is within target; and blueness within:
% Reset pixel if wrong redness OR wrong greenness OR wrong blueness:
raw_frame_size = size( raw_frame )
rows = raw_frame_size( 1 );
columns = raw_frame_size( 2 );
for row = 1:rows
for column = 1:columns
% Reset RGB pixel value if pixel is outside desired range:
pixel_redness = raw_frame(row,column,1);
pixel_greenness = raw_frame(row,column,2);
pixel_blueness = raw_frame(row,column,3);
if ( ( pixel_redness < red_low ) | ( pixel_redness > red_hi ) ...
| ( pixel_greenness < green_low ) | ( pixel_greenness > green_hi ) ...
| ( pixel_blueness < blue_low ) | ( pixel_blueness > blue_hi ) )
raw_frame( row, column, 1 ) = 0;
raw_frame( row, column, 2 ) = 0;
raw_frame( row, column, 3 ) = 0;
end
end
end

From what I can tell is your objective, this is definitely achievable without for loops using logical indexing.
% Prepare Filter Arrays.
imageSize = size ( raw_frame );
filterArray = zeros ( imageSize ( 1 ) , imageSize ( 2 ) );
% Identify filter pixels.
% This step utilizes logical indexing.
filterArray ( raw_frame(:,:,1) < red_low ) = 1;
filterArray ( raw_frame(:,:,1) > red_high ) = 1;
filterArray ( raw_frame(:,:,2) < green_low ) = 1;
filterArray ( raw_frame(:,:,2) > green_high ) = 1;
filterArray ( raw_frame(:,:,3) < blue_low ) = 1;
filterArray ( raw_frame(:,:,3) > blue_high) = 1;
% Replicate the array to 3D.
filter3d = repmat ( filterArray , [ 1 1 3 ]);
% Filter the image.
% This step also uses logical indexing.
raw_frame ( filter3d ) = 0;
Also, it is a generally bad practice to perform color filtering using an RGB image given the inability to isolate color from saturation and darkness using RGB values alone.
For instance, the RGB values [100,80,40] and [50,40,20] are the same color (hue) but different intensity (lightness or value). A better alternative may be to:
targetRgb(1,1,1) = 105;
targetRgb(1,1,2) = 112;
targetRgb(1,1,3) = 175;
targetHsv = rgb2hsv(targetRgb);
frameHsv = rgb2hsv(raw_frame);
frameSize = size(raw_frame);
hTad = 0.05;
% Values must be in the range [0,1] inclusive.
% Because "hue" is a "colorwheel", -0.3 would be the same value as 0.7.
targetHLow = ( targetHsv(1) - hTad + 1 ) - floor ( targetHsv(1) - hTad + 1);
% A value like 1.3 is the same as 0.3
targetHHigh = ( targetHsv(1) + hTad ) - floor ( targetHsv(1) + hTad );
% Create the filter.
hFilter = zeros(frameSize(1),frameSize(2));
hFilter(hFilter<targetHLow) = 1;
hFilter(hFilter>targetHHigh) = 1;
% Zero out the value channel.
frameV = framHsv(:,:,3);
frameV(hFilter) = 0;
frameHsv(:,:,3) = frameV;
% Convert the modified HSV back to RGB.
frameFiltered = hsv2rgb(frameHsv);

I think there is a much simpler, and computationally efficient implementation. The filter is a 2D logical array.
myrgb = rand(100,100,3);
imagesc(myrgb)
floatNorm = 2^8 - 1;
tad = repmat(4, [100, 100, 3]);
target = repmat([105,112,175], 100*100, 1);
target = reshape(target, [100, 100, 3]);
threshold = (target + tad)/floatNorm;
myFilt = abs(myrgb - 1/2) <= threshold;
myrgb(myFilt) = 0;
imagesc(myrgb)

Can't be done without for loop.
So here is my solution using for loops....
% blue: R 105, G 112, B 175
red_target = 105;
green_target = 112;
blue_target = 175;
tad = 20;
red_low = red_target - tad;
red_hi = red_target + tad;
green_low = green_target - tad;
green_hi = green_target + tad;
blue_low = blue_target - tad;
blue_hi = blue_target + tad;
% Filter out non-target colors:
% Pixel redness is within target; greeness is within target; and blueness within:
% Reset pixel if wrong redness OR wrong greenness OR wrong blueness:
raw_frame_size = size( raw_frame )
rows = raw_frame_size( 1 );
columns = raw_frame_size( 2 );
for row = 1:rows
for column = 1:columns
% Reset RGB pixel value if pixel is outside desired range:
pixel_redness = raw_frame(row,column,1);
pixel_greenness = raw_frame(row,column,2);
pixel_blueness = raw_frame(row,column,3);
if ( ( pixel_redness < red_low ) | ( pixel_redness > red_hi ) ...
| ( pixel_greenness < green_low ) | ( pixel_greenness > green_hi ) ...
| ( pixel_blueness < blue_low ) | ( pixel_blueness > blue_hi ) )
raw_frame( row, column, 1 ) = 0;
raw_frame( row, column, 2 ) = 0;
raw_frame( row, column, 3 ) = 0;
end
end
end

Related

how to select specific columns or rows of a table in appdesigner of matlab?

I Want to select a row or column of a table with the edit field component and do some actions on these data and show the result in the first cell of table ([1,1])
rowNames={1:100}
columnName={A:ZZ}
like this:
sum(A1:A20) or Max(AA5:AA10)
I want to write above order in the edit field component
and show the result of them in cell[A,1]
How can I do that?
Here's an implementation that might be similar to what you are attempting to achieve.
Any subset can be calculated using the command such as: Sum(A1:A2)U(B2:B3)
Where A indicates the columns apart of the set and B indicates the rows apart of the set.
Some more test functions include:
Sum(A1:A4) and
Sum(B1:B4)
%Random data%
global Data;
Data = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4];
%Converting data into table%
Data = array2table(Data);
%Grabbing the size of the data%
[Number_Of_Rows, Number_Of_Columns] = size(Data);
%Creating arrays for setting the row and column names%
Row_Labels = strings(1,Number_Of_Rows);
Column_Labels = strings(1,Number_Of_Columns);
for Row_Scanner = 1: +1: Number_Of_Rows
Row_Labels(1,Row_Scanner) = ["B" + num2str(Row_Scanner)];
end
for Column_Scanner = 1: +1: Number_Of_Columns
Column_Labels(1,Column_Scanner) = ["A" + num2str(Column_Scanner)];
end
Row_Labels = cellstr(Row_Labels);
Column_Labels = cellstr(Column_Labels);
%UItable%
Main_Figure = uifigure;
Table = uitable(Main_Figure,'Data',Data);
Table.ColumnName = Column_Labels;
Table.RowName = Row_Labels;
set(Table,'ColumnEditable',true(1,Number_Of_Columns))
%Callback function to update the table%
% Table.CellEditCallback = #(Table,event) Update_Table_Data(Table);
%UIeditfield%
Selection_Field = uieditfield(Main_Figure,'text');
Field_Height = 20;
Field_Width = 100;
X_Position = 350;
Y_Position = 200;
Selection_Field.Position = [X_Position Y_Position Field_Width Field_Height];
Result_Label = uilabel(Main_Figure);
Result_Label.Position = [X_Position Y_Position-100 Field_Width Field_Height];
Selection_Field.ValueChangedFcn = #(Selection_Field,event) Compute_Value(Table,Selection_Field,Result_Label);
%Computing value
function [Data] = Compute_Value(Table,Selection_Field,Result_Label)
Data = Table.Data;
User_Input_Function = string(Selection_Field.Value);
Function = extractBefore(User_Input_Function,"(");
% fprintf("Function: %s \n",Function);
Key_Pairs = extractBetween(User_Input_Function,"(",")");
% fprintf("Key Pairs: (%s)\n", Key_Pairs);
Key_1 = extractBefore(Key_Pairs(1,1),":");
Key_2 = extractAfter(Key_Pairs(1,1),":");
Key_1
Key_2
if length(Key_Pairs) == 2
Key_3 = extractBefore(Key_Pairs(2,1),":");
Key_4 = extractAfter(Key_Pairs(2,1),":");
Key_3
Key_4
end
%Exracting the letters of each key
if contains(Key_1, "A") == 1
% fprintf("Function on columns\n")
Minimum_Column = str2num(extractAfter(Key_1,"A"));
Maximum_Column = str2num(extractAfter(Key_2,"A"));
Table_Subset = Data(1,Minimum_Column:Maximum_Column);
end
if contains(Key_1, "B") == 1
% fprintf("Function on rows\n")
Minimum_Row = str2num(extractAfter(Key_1,"B"));
Maximum_Row = str2num(extractAfter(Key_2,"B"));
Table_Subset = Data(Minimum_Row:Maximum_Row,1);
end
if length(Key_Pairs) == 2
Minimum_Column = str2num(extractAfter(Key_1,"A"));
Maximum_Column = str2num(extractAfter(Key_2,"A"));
Minimum_Row = str2num(extractAfter(Key_3,"B"));
Maximum_Row = str2num(extractAfter(Key_4,"B"));
Table_Subset = Data(Minimum_Row:Maximum_Row,Minimum_Column:Maximum_Column);
end
Table_Subset = table2array(Table_Subset);
%Statements for each function%
if (Function == 'Sum' || Function == 'sum')
fprintf("Computing sum\n");
Result_Sum = sum(Table_Subset,'all');
Result_Sum
Result_Label.Text = "Result: " + num2str(Result_Sum);
end
if (Function == 'Max' || Function == 'max')
fprintf("Computing maximum\n");
Result_Max = max(Table_Subset);
Result_Max
Result_Label.Text = "Result: " + num2str(Result_Max);
end
if (Function == 'Min' || Function == 'min')
fprintf("Computing minimum\n");
Result_Min = min(Table_Subset);
Result_Min
Result_Label.Text = "Result: " + num2str(Result_Min);
end
end

Calculate a confusion matrix given two matrix matlab

I have two Xval(Predicted values) and Sv(validation test) matrices, one with the classifier output data and the other with the validation data for the same samples. Each column represents the predicted value, eg [0 0 1 0 0 0 0 0 0 0] represents digit 3 (1 in the digit that is). I would like to know if it is possible to calculate the confusion matrix in a vectorized way or with a built in function, the sizes of both matrices are 12000x10. The code who generates both matrices are this
load data;
load test;
[N, m] = size(X);
X = [ones(N, 1) X];
[Nt, mt] = size(Xt);
Xt = [ones(Nt, 1) Xt];
new_order = randperm(N);
X = X(new_order,: );
S = S(new_order,: );
part = 0.8;
Xtr = X(1: (part * N),: );
Xv = X((part * N + 1): N,: );
Str = S(1: (part * N),: );
Sv = S((part * N + 1): N,: );
v_c = [];
v_tx_acerto = [];
tx_acerto_max = 0;
c = 250;
w = (X'*X+c*eye(m+1))\X' * S;
Xval = Xv*w;
for i=1:12000
aux = Xval(i,:);
aux(aux == max(aux)) = 1;
aux(aux<1) = 0;
Xval(i,:) = aux;
end
There are build-in functions confusionmat or plotconfusion. But if you want to have full control, you can just write a simple function yourself, e.g:
function [CMat_rel,CMat_abs] = ConfusionMatrix(Cprd,Cact)
Cprd_uq = unique(Cprd);
Cact_uq = unique(Cact);
NumPrd = length(Cprd_uq);
NumAct = length(Cact_uq);
% assert(NumPrd == NumAct)
% allocate memory
CMat_abs = NaN(NumPrd,NumAct);
CMat_rel = NaN(NumPrd,NumAct);
for j = 1:NumAct
lgAct = Cact == Cact_uq(j);
SumAct = sum(lgAct);
for i = 1:NumAct
lgPrd = Cprd == Cact_uq(i);
Num = sum( lgPrd(lgAct) == true );
CMat_abs(i,j) = Num;
CMat_rel(i,j) = Num/SumAct;
end
end
end

Image Transformation Without Loop

I am writing image transformation function in matlab instead of using predefined functions like 'imwarp or imtransform'. I am done with writing the code roughly but it is taking too long due to the use of loop.
Please suggest me a solution which can achieve the same result without loops
function I2 = f_transform(A, I1)
%A is an affine transformation matrix
%I1 is original image to be transformed
s=size(A);
if(s(1)~=3 || s(2)~=3)
disp("Invalid Transforming Matrix");
I2=0;
return;
end
if(det(A)==0)
disp("The given Transforming Matrix is Singular");
I2=0;
return;
end
[r, c]=size(I1);
extremeValues = [1 1 r r ; 1 c 1 c; 1 1 1 1];
newExtremeValues = A * extremeValues;
minRow = floor( min( newExtremeValues(1,:) ) );
maxRow = ceil( max( newExtremeValues(1,:) ) );
minCol = floor( min( newExtremeValues(2,:) ) );
maxCol = ceil( max( newExtremeValues(2,:) ) );
I2 = zeros(maxRow - minRow + 1, maxCol - minCol + 1);
for i = minRow:maxRow
for j=minCol:maxCol
b = A \ [i j 1]';
p = b(1) / b(3);
q = b(2) / b(3);
if(p >= 1 && p <= r && q >= 1 && q <= c)
I2(i - minRow + 1, j - minCol + 1) = I1(round(p),round(q));
end
end
end
I2 = uint8(I2);
return;
end

Filter points using hist in matlab

I have a vector. I want to remove outliers. I got bin and no of values in that bin. I want to remove all points based on the number of elements in each bin.
Data:
d1 =[
360.471912914169
505.084636471948
514.39429429184
505.285068055647
536.321181755858
503.025854206322
534.304229816684
393.387035881967
396.497969729985
520.592172434431
421.284713703215
420.401106087984
537.05330275495
396.715779872694
514.39429429184
404.442344469518
476.846474245118
599.020867750031
429.163139144079
514.941744277933
445.426761656729
531.013596812737
374.977332648255
364.660115724218
538.306752697753
519.042387479096
1412.54699036882
405.571202133485
516.606049132218
2289.49623498271
378.228766753667
504.730621222846
358.715764917016
462.339366699398
512.429858614816
394.778786157514
366
498.760463549388
366.552861126468
355.37022947906
358.308526273099
376.745272034036
366.934599077274
536.0901883079
483.01740134285
508.975480745389
365.629593988233
536.368800360349
557.024236456548
366.776498701866
501.007025898839
330.686029339009
508.395475983019
429.563732174866
2224.68806802212
534.655786464525
518.711297351426
534.304229816684
514.941744277933
420.32368479542
367.129404978681
525.626188464768
388.329756778952
1251.30895065927
525.626188464768
412.313764019587
513.697381733643
506.675438520558
1517.71183364959
550.276294237722
543.359917550053
500.639590923451
395.129864728041];
Histogram computation:
[nelements,centers] = hist(d1);
nelements=55 13 0 0 1 1 1 0 0 2
I want to remove all points apearing less than 5 (in nelements). It means only first 2 elements in nelements( 55, 13 ) remains.
Is there any function in matlab.
You can do it along these lines:
threshold = 5;
bin_halfwidth = (centers(2)-centers(1))/2;
keep = ~any(abs(bsxfun(#minus, d1, centers(nelements<threshold))) < bin_halfwidth , 2);
d1_keep = d1(keep);
Does this do what you want?
binwidth = centers(2)-centers(1);
centersOfRemainingBins = centers(nelements>5);
remainingvals = false(length(d1),1);
for ii = 1:length(centersOfRemainingBins )
remainingvals = remainingvals | (d1>centersOfRemainingBins (ii)-binwidth/2 & d1<centersOfRemainingBins (ii)+binwidth/2);
end
d_out = d1(remainingvals);
I don't know Matlab function for this problem, but I think, that function with follow code is what are you looking for:
sizeData = size(data);
function filter_hist = filter_hist(data, binCountRemove)
if or(max(sizeData) == 0, binCountRemove < 1)
disp('Error input!');
filter_hist = [];
return;
end
[n, c] = hist(data);
sizeN = size(n);
intervalSize = c(2) - c(1);
if sizeData(1) > sizeData(2)
temp = transpose(data);
else
temp = data;
end
for i = 1:1:max(sizeN)
if n(i) < binCountRemove
a = c(i) - intervalSize / 2;
b = c(i) + intervalSize / 2;
sizeTemp = size(temp);
removeInds = [];
k = 0;
for j = 1:1:max(sizeTemp)
if and(temp(j) > a, less_equal(temp(j), b) == 1)
k = k + 1;
removeInds(k) = j;
end
end
temp(removeInds) = [];
end
end
filter_hist = transpose(temp);
%Determines when 'a' less or equal to 'b' by accuracy
function less_equal = less_equal(a, b)
delta = 10^-6; %Accuracy
if a < b
less_equal = 1;
return;
end
if abs(b - a) < delta
less_equal = 1;
return;
end
less_equal = 0;
You can do something like this
nelements=nelements((nelements >5))

Need help for Diploma thesis: calculating preferred direction of gradient vector

i am currently writing my Diploma thesis in Fortran 90. At a certain stage of my code, i simply want to calculate an Integer (should give a direction in a cartesian grid of mesh cells as a result).
There are no error messages, but the result is wrong. What can it be? The value i am looking for is that of the variable idirhf (see below). it should always be either 1, 2, or 3. Depending on the calculated preferred direction. However, it is often idirhf = 0 or maybe even idirhf = 1195 or whatever...the values fcdx, fcdy and fcdz are Real values. they represant the components of a gradient.
All variables are declared in a previous step which is not shown.
Can anybody help? i have already spent hours on that little issue now...
Here is a part of the code:
distz = ( dr ( k ) + dr ( k - 1 ) + dr ( k + 1) ) / 2.
fcdx = ( fv ( i + 1, j, k ) - fv ( i - 1, j ,k ) ) &
& / ( 2. * dz )
fcdy = ( fv ( i, j + 1, k ) - fv ( i, j - 1, k ) ) &
& / ( 2. * dp )
fcdz = ( fv ( i, j, k + 1 ) - fv ( i, j ,k - 1 ) ) &
& / ( distz )
gradf ( 1 ) = ABS ( fcdx )
gradf ( 2 ) = ABS ( fcdy )
gradf ( 3 ) = ABS ( fcdz )
!
! Prüfung in welche der 3 Kooridnatenrichtung x,y,z
! die HF gebildet werden soll:
!
IF ( gradf(1) > gradf(2) ) THEN
IF ( gradf(1) > gradf(3) ) THEN
idirhf = x
x = 1
ider1 = y
ider2 = z
nsten1 = 5
nsten2 = 5
END IF
END IF
!
IF ( gradf(2) > gradf(1) ) THEN
IF ( gradf(2) > gradf(3) ) THEN
idirhf = y
y = 2
ider1 = x
ider2 = z
nsten1 = 4
nsten2 = 5
END IF
END IF
!
IF ( gradf(3) > gradf(1) ) THEN
IF ( gradf(3) > gradf(2) ) THEN
idirhf = z
z = 3
ider1 = x
ider2 = y
nsten1 = 4
nsten2 = 5
END IF
END IF
! WRITE(*,*)'###Nach gradf-Vergleich.idirhf = ', idirhf
Your post doesn't show us the values of x,y,z before they are first used to update idirhf inside your double IF statements. Should the sequence
idirhf = x
x = 1
really be
x = 1
idirhf = x
etc ?