Good Day, Can we convert .txt to .jpg or .png
Show older comments
I want to save a text file as image(jpg or png) but the text contains data (some numbers). Is there any function like Imshow to convert from txt to jpg or png?
Thanks in advance
Answers (3)
MyText = 'Fly! Little white dove, fly!';
img = repmat(uint8(MyText), 20, 5, 3);
imshow(img)
But perhaps what you mean is more like
img = ones(30, 200, 3, 'uint8');
img = insertText(img, [1 1], MyText, 'TextColor', 'red', 'BoxColor', 'black');
imshow(img)
7 Comments
Rayan Shaya
on 6 Jan 2022
Walter Roberson
on 6 Jan 2022
MyText = readlines(filename);
insertText can handle multiple lines.
You have to zoom in a lot to read the image, because the image is going to be pretty big in order to hold all of the numbers. In this case, using these character size estimates, about 2000 pixels high and about 3200 pixels wide -- wider if your numbers need more digits.
40000 numbers is a lot to write into one image: you either have to have a large image or you have to write very very small.
%create a file of data to use for illustration
text_filename = fullfile(tempdir, 'example_data.txt');
example_data = randi([-9 9], 200, 200);
writematrix(example_data, text_filename, 'delimiter', ' ');
%demonstrate that it got written to the file correctly
cmd = sprintf('head -3 "%s"', text_filename);
system(cmd)
%now the work
MyText = readlines(text_filename);
if isempty(MyText{end}); MyText(end) = []; end
num_lines = length(MyText);
maxlinelen = max(cellfun(@length, MyText));
fs = 8;
modifier = 0.53;
char_height = fs + 2;
char_width = modifier*fs + 2;
width_estimate = ceil(char_width * maxlinelen);
height_estimate = ceil(char_height * num_lines);
positions = ones(num_lines, 2);
positions(:,2) = 1 + (0:num_lines-1) * char_height;
img = zeros(height_estimate, width_estimate, 3, 'uint8');
img = insertText(img, positions, MyText, 'TextColor', 'red', 'BoxColor', 'black');
img_filename = fullfile(tempdir, 'example_image.png');
imwrite(img, img_filename);
size(img)
image(img)
Rayan Shaya
on 9 Jan 2022
What is the problem? Is it too large? If so:
- Are there specific constraints on the geometry? (width restriction? height? both?)
- Are there specific requirements for formatting? (columnar format / one row per line / free format)
- What is the numeric formatting required? (floating point? integer? how many digits?)
- What is the smallest acceptable character size?
- Are you trying to resize the image itself with poor results?
As I mentioned, not even the console will display a 200x200 floating-point matrix in columnar format without breaking rows to fit the width. If you want an image that will fit the width of a screen or page at a readable scale, you are going to have to accept the limitations of the geometry.
To put this in perspective, if we assume a maximum image width of 1920px (a screen's width) and with a character size of 8x5px, the numbers (on average) can be no more than 1.9 digits wide, including the spaces between numbers. For example, the following image has 200 single-digit numbers.
For 5px wide digits and spaces, the result is 199*10+5 = 1995px wide.
If full alphanumeric support isn't needed, you can go down to about 6x4px without running into ambiguity. Using an unpublished numeric-only font, the same 200 number vector fits into 1596px.
That's pretty much the lower limit for a fixed-width font. At this scale, you'd better be using lossless file formats.
image_area_pixels = 1280 * 1024
number_of_numbers = 200 * 200
average_pixels_per_number = image_area_pixels / number_of_numbers
height_ratio = 3/2; %characters are taller than they are wide
syms character_width characters_per_number positive
number_width = character_width * characters_per_number
number_height = character_width * height_ratio
number_area = number_width * number_height
average_character_width = simplify(solve(number_area == average_pixels_per_number))
fplot(average_character_width, [1 10])
xlabel('characters per number')
ylabel('character width, pixels');
So if each number is one character wide, then if your characters are more than 4 pixels wide and 6 pixels tall, you cannot fit 200 x 200 in a single 1280 x 1024 image. If each number. A number that is 4 pixels wide is not readable.
Rayan Shaya
on 11 Jan 2022
Besides insertText(), which is a CVT tool, and direct figure capture, there are other text-image tools on the File Exchange:
MIMT has both textim() and textblock(), which generate compact images of text in legacy hardware fonts. (CP437 based)
text2im() by Tobias Kiessling is similar, but only capable of a single font (the same default font used by MIMT textim()) (also CP437 based)
text2im by Rik offers a handful of modern font faces, though it supports a small charset and requires network connection.https://www.mathworks.com/matlabcentral/fileexchange/75021-text2im
text_to_image by Alec Jacobson is more flexible, but uses Imagemagick (external dependency), and is consequently slower.
There are also others:
And there are slightly different approaches:
Using MIMT textim():
mynumber = 1234;
A = textim(sprintf('my favorite password is %d',mynumber),'ibm-iso-16x9');
imwrite(A,'textpicture.png')
From here, the image can be resized (imresize()), padded (padarray() or MIMT addborder()), combined with other images. For compositing with another image, see this:
19 Comments
Rayan Shaya
on 6 Jan 2022
I'm going to stick with MIMT textim() here. I'm using a smaller array size for sake of web-readability, but this should work for any size array. I tailored the formatting to match the output that would be dumped to console when both "compact" and "short" format options are in use. Depending on the contents of your array, you may need to adjust the format string in the call to sprintf (e.g. to change field width, precision, or numeric type).
% this is your array.
% if it's in a file, you'll have to read it using readmatrix() or something
myarray = rand(10);
% convert the array into formatted text in a cell array
% one line per cell
nl = size(myarray,1);
intext = cell(nl+1,1);
intext{1} = 'myarray =';
for r = 1:nl
intext{r+1} = sprintf(repmat('%10.4f',[1 size(myarray,1)]),myarray(r,:).');
end
% rearrange the cell array of chars into a cell array of images
op = {};
for n = 1:nl+1
op{n} = textim(intext{n},'hp-100x-8x6');
end
% edge-concatenate the images, add a 10px border
op = imstacker(op,'gravity','w','padding',0,'dim',1);
op = addborder(op,10);
imshow(op)

Of course, that's an extremely compact font. Using the prior ISO 16x9 font:

The results aren't likely going to be able to perfectly match the console appearance, as textim() doesn't offer whatever particular display font is being used by MATLAB.
Integer arrays
For an example of integer formatting:
myarray = randi([0 100],10);
% this sets the fieldwidth such that the columns are separated
% by no fewer than 3 spaces
maxdigits = floor(log10(max(myarray(:))))+1;
fs = sprintf('%%%dd',maxdigits+3);
nl = size(myarray,1);
intext = cell(nl+1,1);
intext{1} = 'myarray =';
for r = 1:nl
intext{r+1} = sprintf(repmat(fs,[1 size(myarray,1)]),myarray(r,:).');
end
op = {};
for n = 1:nl+1
op{n} = textim(intext{n},'hp-100x-8x6');
end
op = imstacker(op,'gravity','w','padding',0,'dim',1);
op = addborder(op,10);
imshow(op)

Array size considerations:
It's worth noting that for larger arrays, the results won't match console behavior, as the console wraps the output, whereas textim will not. Using the first code with the 8x6 compact font, a 200x200 array will create a 1628x12020 image. That's a pretty large image.
I assume there may be some motivation to reduce the image geometry required to represent the array content. Obviously, the image size also varies with the number formatting specified and the font being used. The smallest font available with textim() is 8x5, but it's not really intended to be very readable.

If the padding and label are not desired, and the field widths don't need to be fixed, some space can be saved by using variable-width columns with fixed spacing. Using the same 8x5 compact font as the prior image:
% columns values vary by order of magnitude
myarray = rand(10).*[1 1 1 10 10 10 100 100 1000 1000];
prec = 4; % number of decimal places
colspace = 1; % spaces between columns
% create format string for variable column widths
fs = '';
for c = 1:size(myarray,2)
maxdigits = max(floor(log10(max(myarray(:,c))))+1,1);
fs = [fs sprintf('%%%d.%df',maxdigits+prec+1+(c~=1)*colspace,prec)];
end
nl = size(myarray,1);
intext = cell(nl,1);
for r = 1:nl
intext{r} = sprintf(fs,myarray(r,:).');
end
% same as before
op = {};
for n = 1:nl
op{n} = textim(intext{n},'everex-me');
end
op = imstacker(op,'gravity','w','padding',0,'dim',1);

A similar approach can be used with integer formatting.
If formatting in columns isn't important and it suffices to just cram all the numbers into a box of some size, that's even simpler. Mat2str() will return the entire array as a single line of text. MIMT textblock() can flow that into a given geometry.
This flows the text into a rectangle of width between 300-400px. The exact width is selected automatically to optimize packing density. This uses default left-justification and does not break numbers.
myarray = rand(10);
textarray = mat2str(myarray,4);
tsize = [NaN 300 400]; % width optimization is optional; see docs
op = textblock(textarray,tsize,'font','everex-me','enablehyph',false);

Do the same thing, but allow lines to break anywhere.
op = textblock(textarray,tsize,'font','everex-me','hardbreak',true,'tightwidth',true,'enablehyph',false);

This is one row per line, close-packed integers.
Again, I'm using the unpublished 6x4 numeric-only font here. This font is not in the version of MIMT I put on the FEX. I attached a modified copy of textim.m and the font file if you want to use it. Just stick tinynum.mat in the MIMT/fonts directory and replace the MIMT textim.m with this one.
myarray = randi([0 9],10); % test data
% if you're reading from a file instead, use something like this
% fname = 'mytestfile.txt';
% myarray = readmatrix(fname,'delimiter',' ');
s = size(myarray);
fs = ['%d' repmat(' %d',[1 s(2)-1])]; % only one space btw numbers
%fs = '%d'; % zero spaces btw numbers
op = cell(s(1),1);
for r = 1:s(1)
thisrow = sprintf(fs,myarray(r,:).');
op{r} = textim(thisrow,'tinynum');
end
op = imstacker(op,'gravity','w','padding',0,'dim',1);
op = op(1:end-1,1:end-1); % trim off 1px pad from character boundaries
imshow(op)

That's 200x200 single-digit positive integers with one space between numbers. Feel free to view that at full scale. It's clear and crisp, but the numbers are very small. The 8x5 and 8x6 fonts may be a readability improvement if the size increase is acceptable. Total image size is 1199x1595.
Repeating with a smaller array for sake of readability, this will work for positive integers
as well as for mixed positive and negative integers

though that obviously reduces the packing density
If you actually want all spaces removed, check the comments and use the alternative format string.
With these small fonts, I would avoid resizing the resultant image. With the 6x4 font, the image cannot be downscaled without becoming unreadable.
op = imresize(op,0.99); % only downscale just a tiny bit

Likewise, avoid saving the image as JPG.
I don't know how small you can manage to go with insertText() without it just turning into a blurry mess.
bg = zeros(15,100);
position = [0 0];
str = '1 2 3 4 5 6 7 8 9';
fontsize = 6;
op = insertText(bg,position,str,'textcolor','white', ...
'boxcolor','black','fontsize',fontsize);
imshow(op)
EDIT:
For sake of absurdity, I've included another font and an updated version of textim. This gets the character size down to 5x4, but it's kind of a chore to read. I doubt it's practical level of readability, but you can play with it if you want. This is the same 10x10 random array:
Rayan Shaya
on 13 Jan 2022
DGM
on 13 Jan 2022
How are they different? Are you using readmatrix() to read it, or are you using something else?
Can you provide an example file?
The image can be inverted easily enough
op = imcomplement(op);
or simply
op = 1-op;
since the image is of class 'double'.
Rayan Shaya
on 13 Jan 2022
char(44)
char(48)
char(49)
DGM's code assumes that you have an array of numbers, but when you readlines() you are getting an array of text containing '0', '1', and ',' and the numeric encodings for those characters are decimal 44, 48, and 49.
Rayan Shaya
on 13 Jan 2022
Walter Roberson
on 14 Jan 2022
Under the circumstance that
myarray = readlines('mytextfile.txt');
then inside DGM's loop, replace
thisrow = sprintf(fs,myarray(r,:).');
with
thisrow = myarray{r};
Is it possible to use something other than readlines(), such that the imported array is numeric? I don't have readlines(), so I can't play with it, but I have a feeling that readmatrix() should work if readlines() does. If your file has redundant field separator characters (e.g. comma and space, or multiple spaces), it'll eat up a bunch of extra horizontal space unless you strrep() them in the string array returned by readlines().
If you want to use the 8x6 HP font, that's fine. That has good character shape for its size. If you wanted tinynum, I included it in the attachments above.
So long as it's just numbers, you could use any of the fonts. So long as the file import and string formatting works, the font shouldn't matter.
Rayan Shaya
on 14 Jan 2022
To create a single-frame GIF:
imwrite(myimage,'myimage.gif');
To create a multiframe (animated) GIF:
imwrite(myfirstframe,'myimage.gif','loopcount',Inf);
imwrite(mysecondframe,'myimage.gif','writemode','append');
% ... and so on
Of course, that's simplifying it a bit. In practice, you'll probably want to also specify 'delaytime' and other options as well. If your inputs are indexed, you'll have to include the map(s). If your frames vary in size, you'll have to deal with that as well. You'd probably want to just make sure they're all the same size.
There's also this for reading/writing multiframe GIF files:
Since that tool accepts inputs as 4D arrays, all frames must be the same size.
I don't know if you're intending to use this on the text images or actual photos like you said. Bear in mind the loss that must come with quantizing a photo down to a 256-color palette (shouldn't be a problem for black & white text though). Also consider that as far as I know, the current versions of imread() cannot correctly read multiframe GIF files any longer, so don't expect to open them back up and edit them.
Rayan Shaya
on 23 Jan 2022
Walter Roberson
on 23 Jan 2022
You might need to use rgb2ind() to create a pseudocolor image and write that.
DGM
on 23 Jan 2022
If the image is nominally grayscale, you can collapse it to a single-channel image with rgb2gray().
If the image has color content, you can use rgb2ind() to convert it to indexed color
[A map] = rgb2ind(A,numberofcolors);
or
[A map] = rgb2ind(A,colormap);
at which point, you'll have to provide imwrite with both the image and the map. Each frame may have its own unique map if desired.
Walter Roberson
on 24 Jan 2022
It turns out that imwrite() is not able to write GIF with a different colormap for each frame. Instead, it converts all frames to use the same colormap as the first frame.
GIF has been extended to allow different colormaps for each frame, but imwrite() does not support that.
Are you sure about that? The legacy behavior of imwrite() does allow unique local color tables to be written. The legacy behavior of imread() is to return all frames, but only the calculated color table for frame 1. It is required to use imfinfo() to retrieve the other color tables in order to get a valid image.
There have been changes to imread() in R2018b and sometime around R2021a which completely break multiframe GIF support. The first bug has a workaround; as far as I know, the second bug has none, as it also affects imfinfo().
If they also broke imwrite() with these latest changes, I guess that's about what I've come to expect. Given that nobody else seems to care that their images are getting trashed, it'll probably stay that way. ... And yes, remapping all the frames to use the same color table is "broken".
Walter Roberson
on 24 Jan 2022
Hmmm... I thought I saw a Mathworks employee say that only a single color table was used. However when I test now
img = zeros(48, 64, 'uint8');
cmaps = [1 0 0; 0 1 0; 0 0 1];
filename = tempname + ".gif"
imwrite(img, cmaps(1,:), filename, 'writemode', 'overwrite', 'loop', inf)
imwrite(img, cmaps(2,:), filename, 'writemode', 'append')
imwrite(img, cmaps(3,:), filename, 'writemode', 'append')
[I, C] = imread(filename, 'frames', 'all');
whos
info = imfinfo(filename)
CT = info.ColorTable
although CT comes out as only suitable for the first frame, when I use my OS programs to examine the image fle, I do clearly see different color of frames, indicating that the local color tables must have been written properly.
DGM
on 25 Jan 2022
Well I guess that's good to know. Since apparently imfinfo() can no longer read any color tables correctly, I didn't have an easy means to check that imwrite() was still working correctly when using the online tools to check the behavior in the newer versions.
yanqi liu
on 6 Jan 2022
clc; clear all; close all;
figure;
% init edit
etf = uicontrol('Style','edit');
uicontrol(etf);
set(etf,'units','normalized','position',[0,0,1,1],'Max',10,'HorizontalAlignment','left','fontsize',14)
% txt
txt = {' I want to save a text file as image(jpg or png) but the text contains data (some numbers)',...
'Is there any function like Imshow to convert from txt to jpg or png?',...
'Thanks in advance!'};
set(etf, 'String', txt);
% get image
f = getframe(gcf);
f = frame2im(f);
figure('Color','w'); imshow(f, []);
% save image
% imwrite(f,'res.png');
1 Comment
Rayan Shaya
on 6 Jan 2022
Categories
Find more on Work with Components in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!




