最近迷上玩拼豆,就想着能不能写个MATLAB版本的拼豆图纸生成器,目前还是一个很简易版本,只支持生成方形图纸,且不太适合生成超大图(色号显示不清晰),大家可以自行添加功能,或做成App designer 形式。

注意这里如果图像四周如果不是透明的,那么白色像素也会被计数,因此底下显示H1色号会需要非常多,因此代码还有改进的空间,之后可以通过检测白色的连通区域,然后看看是否被其他颜色完全包围来决定是否被统计。。。此外之后可能会尝试对轮廓线条进行自动加粗后再生成图纸。。
1 属性设置
代码就直接点击运行,然后选择想要生成图纸的图片即可,图片如果是非正方形会被自动切割。
代码的最前面几行是:
matlab
% 像素画大小; 最大颜色数量
pixN = 50; maxC = 60;
% 是否显示色号, 字体大小
showCName = true; fontSZ = 7;
% 读取色卡
% CData = load('mard188.mat');
CData = load('mard221.mat');
% 色号位置 'bottom'/'right'
palettePos = 'right';
是否显示色号
showCName可以被设置为 true/false,分别有以下效果:


画布尺寸和颜色数量
maxC为最大颜色数量,以下是分别设置为32和8的效果:


pixN是画布尺寸,不建议设置50以上,以下为设置为30的效果:

色卡
色卡这里给了一个188色的mard色卡和一个221色mard色卡,想要用其他牌子的拼豆可以自己制作一个mat文件。
色号位置
可以选择显示在下方还是右侧:


2 其他效果图展示
由于没写轮廓加粗的代码,所以本代码比较适合边缘没那么清晰的图像,此外mard牌子蓝紫色种类比较少,蓝紫色图像绘制效果会稍微差一些:




也可以制作一些游戏角色的拼豆图纸:


3 完整代码
完整代码无配色包文件无法运行,建议从文末链接直接下载包含配色包的压缩包,这里给出代码纯属凑字数。
matlab
% 像素画大小; 最大颜色数量
pixN = 50; maxC = 60;
% 是否显示色号, 字体大小
showCName = true; fontSZ = 7;
% 读取色卡
% CData = load('mard188.mat');
CData = load('mard221.mat');
% 色号位置 'bottom'/'right'
palettePos = 'right';
% 读取图片并预处理
[filename, filepath] = uigetfile({'*.jpg;*.png;*.bmp;*.tiff', 'Image Files'}, 'Select an Image');
[img, ~, alp] = imread(fullfile(filepath, filename));
[M, N, ~] = size(img); img = double(img)./255; alp = double(alp)./255;
szN = min(M, N) - mod(min(M, N), pixN);
img = img((1:szN) + round((M - szN)/2), (1:szN) + round((N - szN)/2), :);
szB = round([szN, szN]/pixN);
imgBR = blockproc(img(:,:,1), szB, @(block) mean(block.data(:)));
imgBG = blockproc(img(:,:,2), szB, @(block) mean(block.data(:)));
imgBB = blockproc(img(:,:,3), szB, @(block) mean(block.data(:)));
if ~isempty(alp)
alp = alp((1:szN) + round((M - szN)/2), (1:szN) + round((N - szN)/2), :);
alpB = blockproc(alp, szB, @(block) mean(block.data(:)));
imgBR(alpB < .5) = 1; imgBG(alpB < .5) = 1; imgBB(alpB < .5) = 1;
end
% 颜色识别
% 首先识别每个像素最接近哪种颜色,统计各种颜色出现次数
% 之后舍去出现次数少的颜色重新再识别一次
RGB = CData.rgb; CN = size(RGB, 1);
dis = + (repmat(imgBR(:), [1, CN]) - repmat(RGB(:,1).', [pixN^2, 1])).^2 ...
+ (repmat(imgBG(:), [1, CN]) - repmat(RGB(:,2).', [pixN^2, 1])).^2 ...
+ (repmat(imgBB(:), [1, CN]) - repmat(RGB(:,3).', [pixN^2, 1])).^2;
[~, indC] = min(dis, [], 2);
countC = sum(repmat(indC, [1, CN]) == repmat(1:CN, [pixN^2, 1]), 1);
[countNC, indNC] = sort(countC, 'descend');
indNC = sort(indNC(1:min(find(countNC > 0, 1, 'last'), maxC)));
CName = CData.palette(indNC, 1); RGB = CData.rgb(indNC, :); CN = length(indNC);
dis = + (repmat(imgBR(:), [1, CN]) - repmat(RGB(:,1).', [pixN^2, 1])).^2 ...
+ (repmat(imgBG(:), [1, CN]) - repmat(RGB(:,2).', [pixN^2, 1])).^2 ...
+ (repmat(imgBB(:), [1, CN]) - repmat(RGB(:,3).', [pixN^2, 1])).^2;
[~, indC] = min(dis, [], 2);
countC = sum(repmat(indC, [1, CN]) == repmat(1:CN, [pixN^2, 1]), 1);
% 创建图窗
fig = figure('WindowState', 'maximized');
ax = axes('Parent',fig, 'DataAspectRatio',[1,1,1], 'NextPlot','add', ...
'Position',[0,0,1,1], 'XColor','none', 'YColor','none');
% 绘制像素
for i = 1:length(indNC)
if all(RGB(i, :) == 1)
else
tind = find(indC == i);
[ti, tj] = ind2sub([pixN, pixN], tind);
ii = repmat(ti, [1, 4]); jj = repmat(tj, [1, 4]);
xx = jj - repmat([0,1,1,0], [size(jj,1), 1]);
yy = pixN + 1 - ii - repmat([0,0,1,1], [size(ii,1), 1]);
fill(xx.', yy.', RGB(i,:))
textC = (mean(RGB(i,:)) < .5).*[1,1,1];
if showCName
text(tj - .5, pixN + .5 - ti, CName{i}, 'FontSize',fontSZ, 'Color',textC, ...
'FontName','Times New Roman', 'HorizontalAlignment','center')
end
end
end
fill([-1,pixN,pixN,-1], [-1,-1,0,0], [17,112,189]./255, 'EdgeColor','none')
fill([-1,-1,0,0], [-1,pixN,pixN,-1], [17,112,189]./255, 'EdgeColor','none')
for i = 1:pixN
text([-.5; i - .5], [i - .5; -.5], num2str(i), 'FontSize',fontSZ+1, 'Color','w', ...
'FontName','Times New Roman', 'HorizontalAlignment','center')
end
% 绘制网格线
xx = [-1:pixN; -1:pixN; nan(1, pixN + 2)];
yy = [zeros(1, pixN + 1) - 1; ones(2, pixN + 1).*pixN];
plot([xx(:); yy(:)], [yy(:); xx(:)], 'LineWidth',.5, 'Color',[0,0,0])
xx = [-1, 0:5:pixN; -1, 0:5:pixN; nan(1,length(0:5:pixN)+1)];
yy = [zeros(1,length(0:5:pixN)+1) - 1; ones(2,length(0:5:pixN)+1).*pixN];
plot([xx(:); yy(:)], [yy(:); xx(:)], 'LineWidth',1, 'Color',[0,0,0])
if strcmp(palettePos, 'bottom')
colN = floor((pixN + 1.5)/(3.5));
for i = 1:length(indNC)
fill([-1,2,2,-1] + mod(i - 1, colN)*3.5, [-1.5,-1.5,-2.5,-2.5] - floor((i-1)/colN)*1.5, RGB(i,:))
textC = (mean(RGB(i,:)) < .5).*[1,1,1];
text(mod(i - 1, colN)*3.5 + .5, - 2 - floor((i-1)/colN)*1.5, [CName{i}, ' (',num2str(countC(i)),')'], ...
'FontSize',fontSZ+1, 'Color',textC, ...
'FontName','Times New Roman', 'HorizontalAlignment','center')
end
else
rowN = floor((pixN + 1.5)/(1.5));
for i = 1:length(indNC)
fill([0,3,3,0] + pixN + .5 + floor((i-1)/rowN)*3.5, pixN + [0,0,-1,-1] - mod(i - 1, rowN)*1.5, RGB(i,:))
textC = (mean(RGB(i,:)) < .5).*[1,1,1];
text(pixN + 2 + floor((i-1)/rowN)*3.5, pixN -.5 - mod(i - 1, rowN)*1.5, [CName{i}, ' (',num2str(countC(i)),')'], ...
'FontSize',fontSZ+1, 'Color',textC, ...
'FontName','Times New Roman', 'HorizontalAlignment','center')
end
end
axis tight
压缩包
- 链接: https://pan.baidu.com/s/1nGbruD4I5x4BZJx3HgY-LQ?pwd=slan
- 提取码: slan