The image you have is messed up somehow. The tiles are actually not consistently sized due to whatever display aliasing is going on. If the tiles were reliably and uniformly spaced, you could use simple operations to isolate the playing field region and then split it directly into an integer number of tiles. Because the tiles are not consistently sized, I'm going to detile the image by addressing a variable-sized window for each tile in the nonuniform grid. It's more complicated, but that's what happens when you're dealing with bad images.
That said, this example doesn't use your nonuniform image, since I wanted to get images with more numbers. It was easier to synthesize test images that way.
Start with a new game image that can be used as a template. Use it to find the information needed to address any other working image.
tpict = imread('mswtemplate.png');
tilefacecolor = [1 1 1]*0.74;
mask = all(abs(im2double(tpict) - permute(tilefacecolor,[1 3 2]))<0.05,3);
mask = imopen(mask,ones(3));
P = regionprops(mask,'area');
tilearea = median([P.Area]);
mask = bwpropfilt(mask,'area',tilearea*(1+[-1 1]*0.08));
mask = imdilate(mask,ones(3));
P = regionprops(mask,'centroid','subarrayidx');
tiling = nnz(p(:,1)>10)+1;
tiling = [numel(P)/tiling tiling];
Now you have information that you can use to detile or otherwise process other images. I doubt OCR is even necessary. You can probably just use color information to determine tile state. In this example, I'm just going to create a state array that gives the tile state as a number representing the number of neighbor mines. An unchecked tile is NaN.
inpict = imread('msw123.png');
mask = all(abs(im2double(inpict) - permute(tilefacecolor,[1 3 2]))<0.05,3);
hsvpict = rgb2hsv(inpict);
ridx = P(k).SubarrayIdx{1};
cidx = P(k).SubarrayIdx{2};
[H S V] = imsplit(hsvpict(ridx,cidx,:));
if range(V(2:end-1,2:end-1)) < 0.01
thismask = ~mask(ridx,cidx);
meanH = circmean(H(thismask)*360);
meanS = mean(S(thismask));
meanV = mean(V(thismask));
[~,state(k)] = min(sum(abs([meanH meanS meanV] - hsvmap),2));
state
state =
NaN 1 0 1 NaN NaN NaN NaN NaN
NaN 1 0 1 2 NaN NaN NaN NaN
1 1 0 0 1 NaN NaN NaN NaN
0 0 0 0 1 2 3 NaN NaN
0 0 0 0 0 0 1 1 1
0 1 1 2 1 1 0 0 0
0 1 NaN NaN NaN 2 1 1 0
0 1 2 NaN NaN NaN NaN 1 0
0 0 1 NaN NaN NaN NaN 1 0
This works for other images as well:
state =
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN 6 NaN NaN NaN NaN NaN 8 NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN 4 2 3 NaN NaN
NaN NaN NaN NaN 3 0 2 NaN NaN
NaN NaN NaN NaN 5 3 4 NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
state =
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN 5 3 5 NaN NaN NaN NaN
NaN NaN 3 0 3 NaN NaN NaN NaN
NaN NaN 5 3 5 NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN 7 NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN
Since the tiling is calculated automatically, it shouldn't matter how large the game field is. You'll just need to have a template image of the same size.
state =
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN 4 NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN 4 NaN NaN
NaN NaN NaN NaN NaN NaN NaN 6 NaN NaN NaN NaN
NaN NaN 3 2 4 NaN NaN NaN NaN NaN NaN NaN
NaN NaN 1 0 3 NaN NaN NaN NaN NaN NaN NaN
NaN NaN 2 1 2 NaN 2 1 3 NaN NaN NaN
NaN NaN NaN NaN NaN NaN 2 0 1 4 NaN NaN
NaN NaN NaN NaN NaN NaN 1 0 0 2 NaN NaN
If the colors vary for your implementation, you'll have to figure out what they are. This makes no attempt to handle flags or mines (in the lost-game state), so if you want to use/detect those, you'll have to figure that out.