How to get the RGB values of each point on a surface when illuminated by 'light' objects?

5 Ansichten (letzte 30 Tage)
Ashraful Haque am 8 Jan. 2023
Beantwortet: DGM am 9 Jan. 2023
I generated a surface with 200x200 points and illuminated it under some light. Now I want to know how the color of each pixel/point is perceived when the surface is viewed from top. I looked at the 'CData' matrix of the surface but it just returns the z-values of the surface. What I want is a 200x200x3 array with RGB values for the second image below and haven't figured out how to do so. I hope my inquiry makes sense.
% generating 200 by 200 surface
m = 200;
n = 200;
x = single(linspace(-8,8,m));
y = single(linspace(-8,8,n));
[X,Y] = meshgrid(x,y);
%Z = (4-abs(X)) + (4-abs(Y));
Z = X.^3-3.*X.*Y.^2;
figure(1)
surf(X,Y,Z,'EdgeColor','none');
figure(2)
s = surf(X,Y,Z);
s.EdgeColor = 'none';
view(0,90)
% making light objects
l1 = light;
l1.Position = [160 400 80];
l1.Style = 'local';
l1.Color = [0 0.8 0.8];
l2 = light;
l2.Position = [.5 -1 .4];
l2.Color = [0.8 0.8 0];
% painting the surface red
s.FaceColor = [0.9 0.2 0.2];
% assigning reflectance values
s.FaceLighting = 'gouraud';
s.AmbientStrength = 0.3;
s.DiffuseStrength = 0.6;
s.BackFaceLighting = 'lit';
s.SpecularStrength = 1;
s.SpecularColorReflectance = 1;
s.SpecularExponent = 7;
0 Kommentare-2 ältere Kommentare anzeigen-2 ältere Kommentare ausblenden

Melden Sie sich an, um zu kommentieren.

Akzeptierte Antwort

DGM am 9 Jan. 2023
Verschoben: DGM am 9 Jan. 2023
I don't have exportgraphics(), and I can't seem to ever get predictably-clean (i.e. unpadded) results from saveas or print, so I'm not bothering with any of those. truesize() doesn't work without an image or texturemapped surface in the axes, so I'm going to ignore that for now as well.
It's hardly elegant, but here's one workaround. This only really works so long as the figure is undocked.
% ... prior plotting stuff
% make sure axes box matches the data extents
xlim([-8 8])
ylim([-8 8])
% make sure the figure is large enough for the sample
set(gcf,'units','normalized');
set(gcf,'position',[0 0 1 1]);
% set size of axes
set(gca,'units','pixels');
set(gca,'position',[100 100 200 200]);
% get screenshot of axes
thisframe = frame2im(getframe(gca));
rawframesize = size(thisframe) % this should match the axes height,width
% instead of trying to reset the figure, i'm just going to close it
close gcf
% show the captured image
imshow(thisframe)
3 Kommentare1 älteren Kommentar anzeigen1 älteren Kommentar ausblenden
DGM am 9 Jan. 2023
Verschoben: DGM am 9 Jan. 2023
I've noticed that it's difficult to get consistent behavior, and I kind of expected that this might behave differently the moment someone else tries to run it.
I suppose it's worth trying to see whether
get(gca,'position')
actually got set to [100 100 200 200], or whether something else is going on.
EDIT: For me, the code worked in R2019b, but the results in R2015b are completely erratic. At least for me, the axes position does get set correctly, but the results returned by getframe are unpredictable in size, aspect ratio, and amount of padding -- even when an explicit RECT property is given. I can't say I have an answer. I'm sure you're not using R2015b, but this only goes to show that when it comes to figure capture, inconsistency is a consistency all in itself.
Ashraful Haque am 9 Jan. 2023
Verschoben: DGM am 9 Jan. 2023
I will play around with your suggestion to see what seems to be the issue but getting an image with larger than desired dimensions is not necessarily a bad thing for my purpose. Also, I don't know how to mark your response as the accepted answer since it's a comment to another answer. If you could post your response an an official answer, that would be great. thanks

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (4)

Walter Roberson am 8 Jan. 2023
Unfortunately you need to use techniques such as exportgraphics or getframe
... Unless you are lucky enough to find something in the File Exchange.
2 KommentareKeine anzeigenKeine ausblenden
Ashraful Haque am 9 Jan. 2023
Thank you for responding. The techniques you mentioned change the number of pixels in the image, hence my RGB array is not the desired dimension. I have been playing with custom resolution option of exportgraphics but no luck so far.
Walter Roberson am 9 Jan. 2023
print allows specification of resolution.

Melden Sie sich an, um zu kommentieren.

Sulaymon Eshkabilov am 8 Jan. 2023
If understood your question correctly, this might be the solution:
...
s.SpecularStrength = 1;
s.SpecularColorReflectance = 1;
s.SpecularExponent = 7;
%% Create an RGB image
RGBim(:,:,1)=X; RGBim(:,:,2) = Y; RGBim(:,:,3)=Z;
imshow(RGBim)
% See pixelvalues
impixelinfo
1 Kommentar-1 ältere Kommentare anzeigen-1 ältere Kommentare ausblenden
Ashraful Haque am 8 Jan. 2023
Thank tou for responding. What I want is a 200x200x3 array that can reproduce the second image in the question with the function imshow. I edited my question for a bit more clarification.

Melden Sie sich an, um zu kommentieren.

Ashraful Haque am 9 Jan. 2023
Bearbeitet: Ashraful Haque am 9 Jan. 2023
Matlab's print and exportgraphics functions allow me to save the top view of the surface plot as an image. And I can get the rgb values from the saved image. But the saved image has a white padding that I can not seem to get rid of. So I ended up using the export_fig function from File Exchange which gets rid of the white padding by default. So I just added the following lines to my original code and got what I wanted. Nonetheless, this is a very roundabout way of solving my issue and I hope someone has a better solution.
% makes the axes invisible
set(gca, 'Visible', 'off')
% extracts the RGB info of the figure
% returns an array with dimensions axbx3
% value of a and b depend on the figure window size on screen
img = export_fig();
% resize the array from axbx3 to 200x200x3
img = imresize(img,[200 200]);
0 Kommentare-2 ältere Kommentare anzeigen-2 ältere Kommentare ausblenden

Melden Sie sich an, um zu kommentieren.

DGM am 9 Jan. 2023
In a sense, this is a non-serious answer, but perhaps consider this as tangential commentary.
For what it's worth, this is what I had to do to get it to behave in R2015b. I don't know if this works for anyone else, but maybe it demonstrates a concept.
% plot some data of arbitrary size
p = peaks(200);
s = pcolor(p);
s.EdgeColor = 'none';
view(0,90)
% make sure axes box matches the data extents
axis tight
% make sure the figure is large enough for the screenshot
set(gcf,'units','normalized');
set(gcf,'position',[0 0 1 1]);
% set size of axes by trial and error
axsz0 = [200 200]; % [x y]
set(gca,'units','pixels');
bg = permute(get(gcf,'color'),[1 3 2])*255; % background color
actualsize = [0 0]; % initialize
axsz = axsz0; % initialize
while any(actualsize ~= axsz0)
set(gca,'position',[100 100 axsz]);
% try to make behavior more consistent?
% neither option seem to work 100%, but both are better
drawnow
%pause(1)
% get frame
thisframe = frame2im(getframe(gca));
% i'm assuming padding occurs on N and E edges (i can't guarantee that)
% i'm using bsxfun() because i'm testing in R2015b
% calculate padding amount and base image geometry
% padding color is only inconsistently equal to figure color
% corners exhibit occasional gray spur pixels
% hence the soft check that at least 98% of pixels match
actualsize = rawframesize-padding % [x y]
% try to correct for padding or improper sizing
axsz = axsz.*axsz0./actualsize;
end
% crop out the good portion of the padded image
% again, i'm assuming padding only occurs on N and E edges
On the first pass, this usually returns an oversize image which, when the padding is removed, is the correct size. The added padding is typically 1-4px. Occasionally, the base geometry or padding will be different (by 10-40px), but the routine will correct for it within about 3-5 attempts. As I mentioned, this doesn't happen for me in R2019b (hooray?). In R2009b, getframe() consistently returns an unpadded image exactly 1px oversize (though other concessions need to be made for the script to work that far back). I have no idea what this will do in a different environment.
One might ask, if getframe() returns a correctly-sized base image with some random additional padding, why not call getframe() with an explicit geometry so as to omit said padding? in R2015b, calling getframe() with an explicit geometry will return a predictably-sized frame with an unpredictable amount of padding. In other words, instead of the raw frame (outer) geometry being random, the base image (inner) geometry is random. I decided to manipulate the axes geometry instead; it solves more potential problems that way anyway.
Am I missing some magic bullet that makes figure capture something other than an unpredictable mess? Something that I can expect works the same way on another computer ... or even the same computer? Something that I can actually recommend to anyone without immediately causing them disappointment?
Cropping and resizing the captured frame after the fact might be sufficient, but the fact that anything so routine involves an unpredictable element is bothersome. Maybe that's just a side effect of dealing with all that's involved in rendering across different platforms.
P.S.
As I use R2009b rarely these days, I've forgotten how much faster it can be at these things.
0 Kommentare-2 ältere Kommentare anzeigen-2 ältere Kommentare ausblenden

Melden Sie sich an, um zu kommentieren.

Kategorien

Mehr zu Graphics Performance finden Sie in Help Center und File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by