Cross stitch in Matlab
A friend of mine said that she might start doing Cross Stitching and that she might want to do a drawing, image, or whatever. At that point I though "Well, I would have to create Matlab code to get any image and convert it to cross stitch." It turns out that I'm not the first to think about it.
But I'm not sure if I'm doing the right thing.
So let's illustrate with an example:
Suppose a pixelated image, any size, any color palette. for example the following screenshot from pixel artist WANEELLA
Suppose we don't want to scale the image, the result image will have the same number of pixels as the original (otherwise a will be executed imresize
).
Now the problem is using only the available color palette. I decided to use the DCM palette mainly because I found an RGB conversion.
I created color quantization. I am using Lab colors to find the closest color in the DCM palette and use that.
clear;clc;
% read image (its a gif)
[img,C]=imread('wn.gif');
% Convert it to RGBimage.
img2=img(:,:,:,3);
imgC=zeros([size(img2) 3]);
for ii=1:size(img2,1)
for jj=1:size(img2,2)
imgC(ii,jj,:)=C(img2(ii,jj)+1,:);
end
end
img=imgC;
imshow(img)
% read DCMtoRB conversion
fid=fopen('DCMRGB.txt');
fgets(fid);
ii=0;
tline = fgets(fid);
while ischar(tline)
ii=ii+1;
table{ii}=tline;
tline = fgets(fid);
end
fclose(fid);
for ii=1:size(table,2)
DCMRGB(ii,1)=str2num(table{ii}(1:4));
DCMRGB(ii,4)=hex2dec(table{ii}(end-5:end-4));
DCMRGB(ii,3)=hex2dec(table{ii}(end-7:end-6));
DCMRGB(ii,2)=hex2dec(table{ii}(end-9:end-8));
end
% origColous=reshape(img, [], 3);
Colours=double(unique(reshape(img, [], 3), 'rows'));
Ncol=size(Colours,1);
cform = makecform('srgb2lab');
DCMLab = applycform(DCMRGB(:,2:4)./255,cform);
Colourslab = applycform(Colours,cform);
eudist=@(p)sqrt(p(:,1).^2+p(:,2).^2+p(:,3).^2);
Cind=zeros(Ncol,1);
for ii=1:Ncol
aux=ones(size(DCMLab,1),3);
aux(:,1)=Colourslab(ii,1);
aux(:,2)=Colourslab(ii,2);
aux(:,3)=Colourslab(ii,3);
d=eudist(DCMLab-aux);
[~,Cind(ii)]=min(d);
end
% now DCMRGB will have DCMcode, R, G, B
% Perform map conversion
img2=zeros(size(img));
indimg=zeros(size(img,1),size(img,2));
for ii=1:size(img,1)
for jj=1:size(img,2)
%wich colour is the pixel?
[~,indx]=ismember(double(squeeze(img(ii,jj,:)))',Colours,'rows');
indimg(ii,jj)=Cind(indx);
img2(ii,jj,:)=DCMRGB(Cind(indx),2:4);
end
end
%%
subplot(121)
imshow((img))
% subplot(222)
% [X_dither,map]=rgb2ind(img,DCMRGB(:,2:4)./255,'nodither');
% imshow(uint16(X_dither),map);
subplot(122)
imshow(double(img2)./255)
The result looks like this:
However, on this web page: http://www.picturecraftwork.com/ As you can see, the color choices are different on the web page and it actually makes more sense and it feels good even without a real color map.
After a lot, I believe there may be two main things that I need to change and implement.
1.- I don't 100% trust the DCMRGB values. I emailed the company looking for more details on theyr color palette.
2.- The values of brightness, contrast, hue and saturation have a huge impact on the output.
how can I change these 4 values (like on a webpage) in the image I want using Matlab?
DMC2RGB file: (you can copy it here if needed)
DCM color palette:
source to share
Okay, here's a generalized proposal, although I haven't had great results yet.
1) Start by reducing the original image to a reasonable number of colors rgb2ind
with using nodither
, specifying only the number of colors (not used card).
[I_ind, old_map] = rgb2ind(I,64,'nodither);
You can experiment with the number of colors for a given image without losing too much detail. Basically you want to figure out how many unique colors you need in the first place (seeing that you are cross-stitching the output you don't need a bunch of colors that are only used for three stitches each).
2) Convert both maps (the ones the output rgb2ind
and the one you load from dcm to the rgb file) to the appropriate color space (I used HSV because I felt lazy L*a*b
probably better).
3) Pick colors by checking which indexed colors have the most pixels, pick the closest one and then remove that color from the candidate list so we end up with 64 or others
% use histogram and sorting - assign colours to most common values first
[plist pbins] = hist(I_ind(:),0:63);
[plist_sorted, sort_ind] = sort(plist, 'descend');
% old_map = the output of rbg2ind
% new_map = the new one we're about to make
% dcm_map = (copy of!) the dcm map in the same colorspace
new_map = zeros(size(old_map));
% loop through from most to least common, remove a colour from the dcm map if we've used it.
for n = 1:sort_ind
D = pdist2(old_map(n,:),dcm_map);
m = find(D==min(D),1,'first');
new_map(n,:) = dcm_map(m,:);
dcm_map(m,:) = [];
end
I used the image you used to answer the question. Since this comes out of rgb2ind
:
How does it end up with 64 DCM colors (bit off, probably because my comparison choice is wrong, but guaranteed to have 64 distinct dcm colors).
I assume you need to find the correct weighting of the different factors (you can easily add the weighting function to pdist2
). For example, weighing the HSV as [0.8, 0.1, 0.1] gave me this brilliant madness:
source to share