Gnoivce and Hartmut helped a lot with this code but it takes a while to run.
The CData property in the bar command doesn't seem to be implemented in the Octave 4.0-4.2.1 version which I'm using. The work around for this was to plot all the single bars individually and set an individual color for each individual bar. People helped me out and got me this far but it takes 5 minutes for the plot to show does anyone know a way of speeding this up?
The following code runs:
marbles.jpg image file used below:
clear all,clf reset,tic,clc
rgbImage = imread('/tmp/marbles.jpg');
hsvImage = rgb2hsv(rgbImage); % Convert the image to HSV space
hPlane = 360.*hsvImage(:, :, 1); % Get the hue plane scaled from 0 to 360
binEdges = 0:360; %# Edges of histogram bins
N = histc(hPlane(:),binEdges); %# Bin the pixel hues from above
C = colormap(hsv(360)); %# create an HSV color map with 360 points
stepsize = 1; % stepsize 1 runs for a while...
for n=binEdges(2:stepsize:end) %# Plot the histogram, one bar each
if (n==1), hold on, end
h=bar(n,N(n));
set(h,'FaceColor',C(n,:)); %# set the bar color individually
end
axis([0 360 0 max(N)]); %# Change the axes limits
set(gca,'Color','k'); %# Change the axes background color
set(gcf,'Pos',[50 400 560 200]); %# Change the figure size
xlabel('HSV hue (in degrees)'); %# Add an x label
ylabel('Bin counts'); %# Add a y label
fprintf('\nfinally Done-elapsed time -%4.4fsec- or -%4.4fmins- or -%4.4fhours-\n',toc,toc/60,toc/3600);
Plot created after 5 mins:
To see original question original question
I'm guessing the loop is the bottleneck in your code that is taking so long? You could remove the loop and create the plot with one call to bar, then call set to modify the hggroup object and its child patch object:
h = bar(binEdges(1:end-1), N(1:end-1), 'histc'); % hggroup object
set(h, 'FaceColor', 'flat', 'EdgeColor', 'none');
hPatch = get(h, 'Children'); % patch object
set(hPatch, 'CData', 1:360, 'CDataMapping', 'direct');
Repeating your code with this fix renders right away for me in Octave 4.0.3:
As I suggested in a comment, I would use image (takes 0.12s on my system for your image).
EDIT: more comments, fix little bug, allow to create bins with stepsize > 1
img_fn = "17S9PUK.jpg";
if (! exist (img_fn, "file"))
disp ("downloading image from imgur.com...");
fflush (stdout);
urlwrite ("http://i.imgur.com/17S9PUK.jpg", "17S9PUK.jpg");
endif
rgbImage = imread (img_fn);
## for debugging so the matrixes fit on screen
if (0)
pkg load image
rgbImage = imresize (rgbImage, [6 8]);
endif
hsvImage = rgb2hsv(rgbImage);
hPlane = 360 .* hsvImage(:, :, 1);
## create bins, I've choosen 2 step to "smooth" the result
binEdges = 1:2:360;
N = histc (hPlane(:), binEdges)';
cm = permute (hsv (numel (binEdges)), [3 1 2]);
## Create an image with x = hue
img = repmat (cm, max(N), 1);
## Create binary mask which is used to black "img" dependent on N
sp = sparse (N(N > 0), (1:360)(N > 0), true, max(N), numel (binEdges));
mask = full (cumsum (flipud (sp)));
## extend mask in depth to suppress RGB
mask = repmat (mask, [1 1 3]);
## use inverted mask to "black out" pixels < N
img(logical (1 - flipud (mask))) = 0;
## show image
image (binEdges, 1:max(N), img)
set (gca, "ydir", "normal");
xlabel('HSV hue (in degrees)');
ylabel('Bin counts');
## print it for stackoverflow
print ("out.png")
Same as above but with bin width 1 (Elapsed time is 0.167423 seconds.)
binEdges = 1:360;
Related
I am testing the validity of the FileExchange project FindPeaksFast with different linewidths and backgrounds.
Test 1 is successful and the tool detects all peaks from 1px to 10 px.
However, Test 2 fails when testing to find peaks on the frame of an object plot i.e. an object (plot) on gray background.
The tool works well on white background.
Code
close all; clear all; clc;
f = figure;
hax = axes(f);
% Comment this out for Test 2
%zeroFigureDecorations(hax);
af = figure('Name', 'Do Not Touch');
x = rand(1,100);
y = rand(1,100);
linewidth=1;
plot(hax, x,y, 'LineWidth', linewidth);
I = getframe(hax);
I = I.cdata;
% https://se.mathworks.com/matlabcentral/fileexchange/37388-fast-2d-peak-finder
p=FastPeakFind(I);
% Input: 344x435x3 uint8
hold(hax, 'on');
plot(hax, p(1:2:end),p(2:2:end),'r+')
hold(hax, 'off');
function zeroFigureDecorations(ax)
axis(ax, 'tight');
set(ax, 'yTickLabel', []);
set(ax, 'xTickLabel', []);
set(ax, 'Ticklength', [0 0]); % http://stackoverflow.com/a/15529630/54964
colormap(ax, 1-gray(1024));
box(ax, 'off');
axis(ax, 'off');
end
Outputs in the following, and Fig. 1 shows that the function can detect something on lines when the background is white but not on correct locations.
Linewidth Output
10 166x1 double
1 844x1 double
Table: full axis decoration in Test 1
Linewidth Output
10 []
1 []
Table: no axis decorations, after zeroFigureDecorations(hax) in Test 2
Fig. 1 line as input (See Bla's answer) and its output,
Fig. 2 Output is wrong in Section 2,
Fig. 3 One more example that you cannot apply to function to simple curves,
Fig. 4 Section 3 Output is wrong, since not known how to apply the function on spectrograms
2 Test with bla's example data
f0 = figure;
hax0 = axes(f0);
d=uint16(conv2(reshape(single( 2^14*(rand(1,128*128)>0.9995) ),[128 128]) ,fspecial('gaussian', 10,2),'same')+2^4*rand(128));
imagesc(d, 'Parent', hax0);
I = getframe(hax0);
I = I.cdata;
p=FastPeakFind(I);
hold(hax0, 'on');
plot(hax0, p(1:2:end),p(2:2:end),'r+')
hold(hax0, 'off');
Output is wrong in Fig. 2
3 Testing with spectrograms
f3 = figure;
hax3 = axes(f3);
N = 1024*10;
n = 0:N-1;
w0 = 2*pi/5;
x = sin(w0*n)+10*sin(2*w0*n);
s = spectrogram(x);
spectrogram(x,'yaxis')
p=FastPeakFind(s);
hold on;
plot(p(1:2:end),p(2:2:end),'r+')
Matlab: 2016b
OS: Debian 8.5
You are not using the function correctly.
your code is this (verbatim):
f = figure;
hax = axes(f);
af = figure('Name', 'Do Not Touch');
x = rand(1,100);
y = rand(1,100);
linewidth=1;
plot(hax, x,y, 'LineWidth', linewidth);
I = getframe(hax);
I = I.cdata;
The matrix I is not a matrix that contain peaks like the function is intended to have. This is how it looks like:
imagesc(I);
Even if all you had were single pixels, that is not what the function is supposed to have, as it is said that the peaks point spread function needs to be larger than some # of pixels, and that they are assumed to be sparse. The function has a demonstration on a sample image that works fine.
Also , it's completely unclear what you even mean by peaks here.
EDIT:
Here's an example of how to use the function. First let's select random positions where we "create" the peaks:
I=rand(200)>0.9995;
This makes a binary matrix with only the points larger than 0.9995 selected (or having value 1). At each step you can imagesc(I) to see how I looks.
In real life, a camera will have some intensity in these points so we write:
I=I*100;
This is important as the peak by dentition needs to be a maximum value in its neighborhood. In real life, peaks are mostly not single pixels, they have some "width" or spread (this is also what the function says it deals with):
I=conv2(I,fspecial('gaussian',10,2),'same');
here, this spread is done by a "point-spread function" of a guassian of some width.
Lets add some 30% noise (note that after the last step the maximum value of the peaks is no longer 100, because it is spread to other pixels as well):
I=I+0.3*max(I(:))*rand(size(I));
Let's find peaks
p=FastPeakFind(I);
See how it did:
subplot(1,2,1);imagesc(I);
subplot(1,2,2);imagesc(I); hold on
plot(p(1:2:end),p(2:2:end),'r+')
In the function code, the example is doing what I wrote here in a single line. Note that there is an edg parameter, as this will not work on peaks on the edges of the image. This cab be solved by padding the image with zeros I think...
I'm using K-means clustering in color-based image segmentation. I have a 2D image which has 3 colors, black, white, and green. Here is the image,
I want K-means to produce 3 clusters, one represents the green color region, the second one represents the white region, and the last one represents the black region.
Here is the code I used,
%Clustering color regions in an image.
%Step 1: read the image using imread, and show it using imshow.
img = (imread('img.jpg'));
figure, imshow(img), title('X axis rock cut'); %figure is for creating a figure window.
text(size(img,2),size(img,1)+15,...
'Unconventional shale x axis cut', ...
'FontSize',7,'HorizontalAlignment','right');
%Step 2: Convert Image from RGB Color Space to L*a*b* Color Space
conversionform = makecform('srgb2lab'); %the form of the conversion is defined as from rgb to l a b
lab_img = applycform(img,conversionform); %converting the rgb image to l a b image using the conversion form defined above.
%Step 3: Classify the Colors in 'a*b*' Space Using K-Means Clustering
ab = double(lab_img(:,:,2:3));
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);
nColors = 3;
% repeat the clustering 3 times to avoid local minima
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
'Replicates',3);
%Step 4: Label Every Pixel in the Image Using the Results from KMEANS
%For every object in your input, kmeans returns an index corresponding to a cluster. The cluster_center output from kmeans will be used later in the example. Label every pixel in the image with its cluster_index.
pixel_labels = reshape(cluster_idx,nrows,ncols);
figure, imshow(pixel_labels,[]), title('image labeled by cluster index');
segmented_images = cell(1,3);
rgb_label = repmat(pixel_labels,[1 1 3]);
for k = 1:nColors
color = img;
color(rgb_label ~= k) = 0;
segmented_images{k} = color;
end
figure, imshow(segmented_images{1}), title('objects in cluster 1');
figure, imshow(segmented_images{2}), title('objects in cluster 2');
figure, imshow(segmented_images{3}), title('objects in cluster 3');
But I'm not getting the results as required. I get one cluster with green regions, one cluster with green region boundaries, and one with gray, black, and white colors. Here are the resulting clusters.
The aim of doing this is that after getting the correct clustering results, I want to count the number of pixels in every region using the concept of connected components.
So, my aim is to know how many pixels there are in every color region. I tried another simpler way by getting the matrix of the 2D image and trying to figure out the number of pixels for every color. However, I found more than 3 RGB colors in the matrix, maybe because pixels of the same color have a slightly different color levels. That's why I went to image segmentation.
Can anyone please tell me how to fix the code above in order to get the required results?
I would also appreciate it if you give me hints on how to do this in an easier way, if there is any.
EDIT: Here is a code I made to iterate over every pixel in the image. Please notice I use 4 colors red, yellow, blue, and white instead of green, white, and black, but the idea is the same. rgb2name is the function that returns the color name given RGB color.
im= imread ('img.jpg');
[a b c] = size (im);
%disp ([a b]);
yellow=0;
blue=0;
white=0;
red=0;
for i=1:a
for j=1:b
x= impixel(im, i, j)/255 ;
color= rgb2name (x);
if (~isempty (strfind (color, 'yellow')))
yellow= yellow+1;
elseif (~isempty (strfind(color, 'red')))
red= red+1;
elseif (~isempty (strfind (color, 'blue')))
blue= blue+1;
elseif (~isempty (strfind (color, 'white')))
white= white+1;
else
%disp ('warning'); break;
end
disp (color);
disp (i);
end
end
disp (yellow)
disp (red)
disp (blue)
disp (white)
Thank You.
I thought this problem was very interesting, so I apologize ahead of time if the answer is a little overboard. In short, k-means is the right strategy, in general, for problems where you want to segment an image into a discrete color space. But, your example image, which contains primarily only three colors, each of which is well separated in color space, is easily segmented using only a histogram. See below for segmenting using thresholds.
You can easily get the pixel counts by summing each matrix. e.g., bCount = sum(blackPixels(:))
filename = '379NJ.png';
x = imread(filename);
x = double(x); % cast to floating point
x = x/max(max(max(x))); % normalize
% take histogram of green dimension
g = x(:, :, 2);
c = hist(g(:), 2^8);
% smooth the hist count
c = [zeros(1, 10), c, zeros(1, 10)];
N = 4;
for i = N+1:length(c) - N;
d(i - N) = mean(c(i -N:i));
end
d = circshift(d, [1, N/2]);
% as seen in histogram, the three colors fall nicely into 3 peaks
figure, plot(c, '.-');
[~, clusterCenters] = findpeaks(d, 'MinPeakHeight', 1e3);
% set the threshold halfway between peaks
boundaries = [floor((clusterCenters(2) - clusterCenters(1))/2), ...
clusterCenters(2) + floor((clusterCenters(3) - clusterCenters(2))/2)];
thresh1 = boundaries(1)*ones(size(g))/255;
thresh2 = boundaries(2)*ones(size(g))/255;
% categorize based on threshold
blackPixels = g < thresh1;
greenPixels = g >= thresh1 & g < thresh2;
whitePixels = g >= thresh2;
This is my approach to count the number of pixels in every region. Given that (as discussed in the comments):
the value (RGB) and the number (K) of colors are known a priori
compression artifacts and anti-aliasing generated additional colors, that must be considered as the nearest-neighbor among the K know colors.
Since you know a priori the colors, you don't need k-means. It could actually lead to bad results as in your question. The approach of #crowdedComputeeer take care of this aspect.
You can compute nearest neighbor with pdist2 directly on the pixel values. There's no need to use the really slow function that looks for the color name.
Here is the code. You can change the number and values of colors simply modifying the variable colors. This will compute the number of pixels in each color, and output the masks.
img = (imread('path_to_image'));
colors = [ 0 0 0; % black
0 1 0; % green
1 1 1]; % white
% % You can change the colors
% colors = [ 0 0 1; % red
% 1 1 0; % yellow
% 1 0 0; % blue
% 1 1 1]; % white
% Find nearest neighbour color
list = double(reshape(img, [], 3)) / 255;
[~, IDX] = pdist2(colors, list, 'euclidean', 'Smallest', 1);
% IDX contains the indices to the nearest element
N = zeros(size(colors, 1), 1);
for i = 1 : size(colors, 1)
% Count the number of pixels for each color
N(i) = sum( IDX == i );
end
% This will display the number of pixels for each color
disp(N);
% Eventually build the masks
indices = reshape(IDX, [size(img,1), size(img,2)]);
figure();
szc = size(colors,1);
for i = 1 : szc
subplot(1,szc,i);
imagesc(indices == i);
end
Resulting counts:
97554 % black
16894 % green
31852 % white
Resulting masks:
Maybe this project could help, please take a try.
Is there an easy way to put a bitmap image in the background of a Matlab plot which does not fill the whole available space und keeps its aspect ratio when the figure is resized?
TIA
I'm not quite sure to understand what you mean by
plot which does not fill the whole available space
however the following solution should help you solve your problem (or at least get you started).
Basically read an image (here grayscale) and display it using the imagesc command along with the grayscale colormap, then issue the hold on command and plot the data. Notice that you need to reverse the direction of the x-axis in order to get the right direction for the plot.
Here is the code:
clear
clc
close all
A = imread('cameraman.tif');
x = 1:10;
y = x;
figure
%// Notice the fliplr(A) to reverse the direction of the x data
imagesc([min(x(:)) max(x(:))], [min(y(:)) max(y(:))],fliplr(A));
colormap gray
%// Here reverse the direction of the x axis, otherwise the plot is
%// reversed
set(gca,'XDir','reverse')
hold on
plot(x,y,'--r')
axis off
And the result:
If your background image is RGB, you can use the image function: (modified from answer here): You need to flip the x data from the image for each channel separately, because fliplr only accepts 2D arguments:
DataXImage = linspace(min(x), max(x), size(A, 2));
DataYImage = linspace(min(y), max(y), size(A, 1));
%// flip dimensions for each channel
B = cat(3,fliplr(A(:,:,1)),fliplr(A(:,:,2)),fliplr(A(:,:,3)));
image(DataXImage, DataYImage, B, 'CDataMapping', 'scaled');
which, using the peppers.png image, gives this:
Is this what you had in mind? If not please tell me!
img = imread('myimage.png');
% set the range of the axes
% The image will be stretched to this.
min_x = 0;
max_x = 8;
min_y = 0;
max_y = 6;
% make data to plot - just a line.
x = min_x:max_x;
y = (6/8)*x;
imagesc([min_x max_x], [min_y max_y], img);
hold on;
plot(x,y);
I'm using the code on the MatLab website, "Color-Based Segmentation Using the Lab* Color Space":
http://www.mathworks.com/help/images/examples/color-based-segmentation-using-the-l-a-b-color-space.html
So I'm trying to select some areas myself instead of using the "load region_coordinates", using roipoly(fabric), but i get stuck. How do I save coordinates of the polygon I just drew? I'm actually following advice from lennon310 at Solution II, bottom of page:
A few questions about color segmentation with L*a*b*
I'm not sure when to save region_coordinates and do size(region_coordinates,1)
I made the following changes (Step 1):
1) Removed "load region_coordinates"
2) Added "region_coordinates = roipoly(fabric);"
Here's the code:
`
%% Step 1
fabric = imread(file);
figure(1); %Create figure window. "If h is not the handle and is not the Number property value of an existing figure, but is an integer, then figure(h) creates a figure object and assigns its Number property the value h."
imshow(fabric)
title('fabric')
%load regioncoordinates; % 6 marices(?) labelled val(:,:,1-6), 5x2 (row x column)
region_coordinates = roipoly(fabric);
nColors = 6;
sample_regions = false([size(fabric,1) size(fabric,2) nColors]); %Initializing an Image Dimension, 3x3 (:,:,:) to zero? Zeros() for arrays only I guess.
%Size one is column, size two is row?
for count = 1:nColors
sample_regions(:,:,count) = roipoly(fabric,region_coordinates(:,1,count),region_coordinates(:,2,count));
end
figure, imshow(sample_regions(:,:,2)),title('sample region for red');
%%Step 2
% Convert your fabric RGB image into an L*a*b* image using rgb2lab .
lab_fabric = rgb2lab(fabric);
%Calculate the mean a* and b* value for each area that you extracted with roipoly. These values serve as your color markers in a*b* space.
a = lab_fabric(:,:,2);
b = lab_fabric(:,:,3);
color_markers = zeros([nColors, 2]);%... I think this is initializing a 6x2 blank(0) array for colour storage. 6 for colours, 2 for a&b colourspace.
for count = 1:nColors
color_markers(count,1) = mean2(a(sample_regions(:,:,count))); %Label for repmat, Marker for
color_markers(count,2) = mean2(b(sample_regions(:,:,count)));
end
%For example, the average color of the red sample region in a*b* space is
fprintf('[%0.3f,%0.3f] \n',color_markers(2,1),color_markers(2,2));
%% Step 3: Classify Each Pixel Using the Nearest Neighbor Rule
%
color_labels = 0:nColors-1;
% Initialize matrices to be used in the nearest neighbor classification.
a = double(a);
b = double(b);
distance = zeros([size(a), nColors]);
%Perform classification, Elucidean Distance.
for count = 1:nColors
distance(:,:,count) = ( (a - color_markers(count,1)).^2 + (b - color_markers(count,2)).^2 ).^0.5;
end
[~, label] = min(distance,[],3);
label = color_labels(label);
clear distance;
%% Step 4: Display Results of Nearest Neighbor Classification
%
% The label matrix contains a color label for each pixel in the fabric image.
% Use the label matrix to separate objects in the original fabric image by color.
rgb_label = repmat(label,[1 1 3]);
segmented_images = zeros([size(fabric), nColors],'uint8');
for count = 1:nColors
color = fabric;
color(rgb_label ~= color_labels(count)) = 0;
segmented_images(:,:,:,count) = color;
end
%figure, imshow(segmented_images(:,:,:,1)), title('Background of Fabric');
%Looks different somehow.
figure, imshow(segmented_images(:,:,:,2)), title('red objects');
figure, imshow(segmented_images(:,:,:,3)), title('green objects');
figure, imshow(segmented_images(:,:,:,4)), title('purple objects');
figure, imshow(segmented_images(:,:,:,5)), title('magenta objects');
figure, imshow(segmented_images(:,:,:,6)), title('yellow objects');
`
You can retrieve the coordinates of the polygon using output arguments in the call to roipoly. You can then get a binary mask of the polygon, as well as vertices coordinates if you want.
Simple example demonstrating:
clear
clc
close all
A = imread('cameraman.tif');
figure;
imshow(A)
%// The vertices of the polygon are stored in xi and yi;
%// PolyMask is a binary image where pixels == 1 are white.
[polyMask, xi, yi] = roipoly(A);
This looks like this:
And if you wish to see the vertices with the binary mask:
%// display polymask
imshow(polyMask)
hold on
%// Highlight vertices in red
scatter(xi,yi,60,'r')
hold off
Which gives the following:
So to summarize:
1) The polygon's vertices are stored in xi and yi.
2) You can plot the binaryMask of the polygon using imshow(polyMask).
3) If you need the coordinates of the white pixels, you can use something like this:
[row_white,col_white] = find(polyMask == 1);
You're then good to go. Hope that helps!
I need to create an nth-order Hadamard matrix, row double it, within each row randomly permute the elements of the matrix, and then display it. So far, I have accomplished all of these things. What I end up with when I imshow(matrix) is a nice picture of black and white boxes. But I haven't figured out how to insert a fine line to divide each row. I can create something like the first image on the left, but not the image on the right (these are Figures 1 and 2 from this paper)
Any help or comments would be thoroughly appreciated.
I've found using vector approaches (e.g., patch and rectangle) for this sort of problem unnecessarily challenging. I think that it's more straightforward to build a new image. This avoids floating-point rounding issues and other things that crop up with vector graphics. My solution below relies on some functions in the Image Processing Toolbox, but is simple and fast:
% Create data similarly to #TryHard
H = hadamard(48);
C = (1+[H;-H])/2;
rng(0); % Set seed
C(:) = C(randperm(numel(C))); % For demo, just permute all values, not rows
% Scale image and lines
scl = 10; % Amount to vertically scale each row
pad = 2; % Number of pixels to add between each row
C = imresize(C,scl,'nearest');
C = blockproc(C,[scl size(C,2)],#(x)[x.data;zeros(pad,size(C,2))]);
C = C(1:end-pad,:); % Remove last line added
% Dispay image
imshow(C)
This results in an image like this
The scl and pad parameters can be easily adjusted to obtain different sizes and relative sizes. You can call imresize(...,'nearest') again after adding the lines to further scale the image if desired. The blocproc line could potentially be made more efficient with various options (see the help). It could also be replaced by calls to im2col and col2im, which possibly could be faster, if messier.
I did not try the code, but I think that something like that should work:
sizeOfACube = 6;
numberOfRows = 47;
RGB = imread('image.png');
RGB = imresize(A, [(numRows+numberOfRows) numCols]);
for i=1:1:NumberOfRows
RGB(i*6,:,:) = 0;
end
imagesc(RGB);
imwrite(RGB,'newImage.png');
with:
sizeOfAcube the size of one cube on the QRcode.
numRows and numCols the number of Rows and Column of the original image.
One solution is to use patches, for instance as follows:
% set up example array
xl = 24; yl = xl;
[X Y] = find(hadamard(xl)==1);
% generate figure
figure, hold on
for ii=1:length(X)
patch(X(ii) + [0 0 1 1],Y(ii) + [0.1 0.9 0.9 0.1],[1 1 1],'Edgecolor',[1 1 1])
end
axis([0 xl+1 0 yl+1])
axis('square')
The patch command patch(x,y, color) accepts the vertices of the polygon element as x and y. In this example you can modify the term [0.1 0.9 0.9 0.1] to set the thickness of the bounding black line.
This generates
Edited
For the particular instance provided by the OP:
H=Hadamard(48); %# now to row-double the matrix
A=(1+H)/2;
B=(1-H)/2;
C=[A; B]; %# the code below randomly permutes elements within the rows of the matrix
[nRows,nCols] = size(C);
[junk,idx] = sort(rand(nRows,nCols),2); %# convert column indices into linear indices
idx = (idx-1)*nRows + ndgrid(1:nRows,1:nCols); %# rearrange whatever matrix
E = C;
E(:) = E(idx);
[X Y] = find(logical(E));
xl = length(X);
yl = length(Y);
figure, hold on
for ii=1:xl
rectangle('Position',[X(ii) Y(ii)+.2 1 0.8],'facecolor',[1 1 1],'edgecolor',[1 1 1])
end
axis([0 max(X)+1 0 max(Y)+1])
axis('square')
set(gca,'color',[0 0 0])
set(gca,'XTickLabel',[],'YTickLabel',[],'XTick',[],'YTick',[])
This example uses rectangle instead of patch to generate sharp corners.
The image: