If I run the code below, I get the following values for the tm and gm structures with the "cambria Math" font:
tm.tmHeight = 161
tm.tmAscent = 90
tm.tmDescent = 71
and
gm.gmBlackBoxY = 14
The values in tm are clearly in error! The gmBlackBoxY seems to be correct.
Now, if I run the code with
lfFaceName = "Arial"
I get for tm and gm the following values, which are correct:
tm.tmHeight = 33
tm.tmAscent = 27
tm.tmDescent = 6
and
gm.gmBlackBoxY = 15
Code:
int iLogPixelsY;
iLogPixelsY = GetDeviceCaps(hdc,LOGPIXELSY);
LOGFONT lf;
int iPts;
iPts = 22;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfHeight = -iPts * iLogPixelsY / 72;
lf.lfWeight = FW_NORMAL;
lf.lfItalic = 0;
lf.lfCharSet = 0;
lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
wcscpy(lf.lfFaceName, L"Cambria Math");
HFONT hFont;
hFont = CreateFontIndirect(&lf);
hFont = (HFONT)SelectObject(hdc, hFont);
TCHAR tx;
tx = 'a';
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
GLYPHMETRICS gm;
GetGlyphOutline(hdc, tx, GGO_METRICS, &gm, 0, NULL, &gmat);
Could anyone explain the apparent incorrectness in obtaining the TEXTMETRIC structure for a "Cambria Math" font ?
Incorrectness in your code is does not apply to obtaining TEXTMETRIC structure (excluding, that you use TCHARs, CHARs and WCHARs functions and variables in same code).
tm.tmHeight == 161;
tm.tmAscent == 90;
tm.tmDescent == 71;
tm.tmInternalLeading == 132;
Above lines do not have any errors!!!
tm.tmHeight == tm.tmAscent + tm.tmDescent;
tm.tmHeight == tm.tmInternalLeading + MulDiv(22,GetDeviceCaps(hdc,LOGPIXELSY),72);
"Cambria Math" is designed with these parameters!
Go to the link http://www.ascenderfonts.com/font/cambria-regular.aspx Change the font size to 22pt (or whatever) and look at the difference between of top and bottom margins for the font "Cambria" and font "Cambria Math".
Related
i have octave code who convert image into specific code on a game, it work well until file number 1507
% The images must be in the same folder as this script.
% The originY variable will be updated for each PNG file.
originY = 3010000;
% Threshold values modify which shades of grey are consirered scenery (0-255).
threshold1 = 50; %black is 0
threshold2 = 0; %white is 255
% The origin modifies where the rider begins relative to the image.
originX = 200;
% Images can be made larger using the scale option. Use only whole numbers.
% Note that there is a size limit for tracks uploaded to FRHD.
scaleX = 2;
scaleY = 2;
% Loop through each PNG file until 2154.png.
for n = 1505:2154
% Create the file name for the current PNG file.
filename = sprintf('%04d.png', n);
% Read the PNG file.
image = imread(filename);
% Create the file name for the output text file.
outputFilename = sprintf('%04d.txt', n);
% Open the output text file for writing.
output = fopen(outputFilename, 'w');
grayImage = transpose(rgb2gray(image));
sceneryString = sprintf('#');
for j = 1:(size(grayImage,2)*scaleY)
currentColor = 2; % 2 for white, 1 for gray, 0 for black
for i = 1:size(grayImage,1)
if originY+j >= 0
row = lower(dec2base(originY+j, 32));
else
row = strcat('-', lower(dec2base(-(originY+j), 32)));
end;
if originX+i*scaleX >= 0
xPos = lower(dec2base(originX+i*scaleX, 32));
else
xPos = strcat('-', lower(dec2base(-(originX+i*scaleX), 32)));
end;
pixel = grayImage(i,ceil(j/scaleY));
if currentColor == 2
if pixel > threshold2
continue;
elseif pixel < threshold1
fprintf(output, '%s %s ', xPos, row);
currentColor = 0;
else
sceneryString = sprintf('%s%s %s ', sceneryString, xPos, row);
currentColor = 1;
end;
elseif currentColor == 0
if pixel < threshold1
continue;
elseif pixel > threshold2
fprintf(output, '%s %s,', xPos, row);
currentColor = 2;
else
fprintf(output, '%s %s,', xPos, row);
sceneryString = sprintf('%s%s %s ', sceneryString, xPos, row);
currentColor = 1;
end;
else
if pixel > threshold2
sceneryString = sprintf('%s%s %s,', sceneryString, xPos, row);
currentColor = 2;
elseif pixel < threshold1
sceneryString = sprintf('%s%s %s,', sceneryString, xPos, row);
fprintf(output, '%s %s ', xPos, row);
currentColor = 0;
else
continue;
end;
end;
end;
if currentColor == 1
sceneryString = sprintf('%s%s %s,', sceneryString, xPos, row);
elseif currentColor == 0
fprintf(output, '%s %s,', xPos, row);
end;
end;
sceneryString = sprintf('%s#', sceneryString);
fprintf(output, '%s', sceneryString);
fclose(output);
% Increase the value of originY by 2000 for the next iteration of the loop.
originY = originY + 2000;
end;
error message
error: rgb2gray: RGB of invalid data type 'logical'
error: called from
colorspace_conversion_input_check at line 41 column 7
rgb2gray at line 61 column 5
FreeRiderImageMaker at line 31 column 15
i try to start it again from 1505, but its still stop at 1507. i change the originY from 0 into 3010000 so the output coordinate is correct, but its still not working, anyone can help me fix this please??
I have a cell with 3 synchronized signals in each column. I want to extract specific segments into my seg cell. How do I get rid of the loop here?
data = cell(1, 3);
data{1,1} = [ones(30, 1)];
data{1,2} = [1:30]';
data{1,3} = 2*[1:30]';
ind1 = [1; 11; 17];
ind2 = [10; 16; 30];
for i = 1:3
seg{i, 1} = [data{:,1}(ind1(i):ind2(i)) data{:,2}(ind1(i):ind2(i)) data{:,3}(ind1(i):ind2(i))];
end
You mean like this?
data = cell(1, 3);
data{1,1} = ones(30, 1);
data{1,2} = (1:30)';
data{1,3} = 2*(1:30)';
D = cell2mat(data);
Cel = mat2cell(D,[10 6 14],3);
I got a 16 bpp bitmap that I converted to 32 bpp via code below:
void Rgb555ToRgb8(const UChar* bitmapData, UInt32 width, UInt32 height, UChar* buf)
{
UInt32 dst_bytes_per_row = width * 4;
UInt32 src_bytes_per_row = ((width * 16 + 31) / 32) * 4;
UInt16 red_mask = 0x7C00;
UInt16 green_mask = 0x3E0;
UInt16 blue_mask = 0x1F;
for (UInt32 row = 0; row < height; ++row)
{
UInt32 dstCol = 0, srcCol = 0;
do
{
UInt16 rgb = *(UInt16*)(bitmapData + row * src_bytes_per_row + srcCol);
UChar red_value = (rgb & red_mask) >> 10;
UChar green_value = (rgb & green_mask) >> 5;
UChar blue_value = (rgb & blue_mask);
buf[row*dst_bytes_per_row + dstCol] = blue_value << 3;
buf[row*dst_bytes_per_row + dstCol + 1] = green_value << 3;
buf[row*dst_bytes_per_row + dstCol + 2] = red_value << 3;
buf[row*dst_bytes_per_row + dstCol + 3] = rgb >> 15;
srcCol += 2;
dstCol += 4;
} while (srcCol < src_bytes_per_row);
}
}
Here is conversion result: [2]: https://i.stack.imgur.com/1ajO7.png
I also tried to convert this image via GdiPlus:
Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(w,h,PixelFormat32bppRGB);
Resultant image is .
Notice that the 2 results don't look exactly the same (e.g., the background in GdiPlus result is white). How can I modify my code to match GdiPlus result?
There are two issues that need to be addressed:
Unused bits
When moving from 5 bits of information to 8 bits of information you gain an additional 3 bits. As implemented, the code doesn't make use of that additional range, and is biased towards darker color components. This is an illustration of what blue_value << 3 actually does:
5 bits per channel 8 bits per channel
bbbbb -> bbbbb000
To address this, the least significant 3 bits need to grow as the channel value gets higher. A simple (yet somewhat inaccurate) would be to just copy the most significant 3 bits down to the least significant 3 bits, i.e.
buf[row*dst_bytes_per_row + dstCol] = (blue_value << 3) | (blue_value >> 2);
buf[row*dst_bytes_per_row + dstCol + 1] = (green_value << 3) | (green_value >> 2);
buf[row*dst_bytes_per_row + dstCol + 2] = (red_value << 3) | (red_value >> 2);
The exact mapping would be a bit more involved, something like
blue_value = static_cast<UChar>((blue_value * 255.0) / 31.0 + 0.5);
That converts from 5 bits to the respective 8 bit value that's nearest to the ideal value, including the 4 values that were 1/255th off in the bit-shifting solution above.
If you opt for the latter, you can build a lookup table that stores the mapped values. This table is only 32 entries of one byte each, so it fits into a single cache-line.
Alpha channel
Assuming that the MSB of your source image is indeed interpreted as an alpha value, you're going to have move that into the destination as well. Since the source is only 1 bit of information, the raw transformation is trivial:
buf[row*dst_bytes_per_row + dstCol + 3] = rgb & (1 << 15) ? 255 : 0;
That may or may not be all that's needed. Windows assumes premultiplied alpha, i.e. the stored values of the color channels must be premultiplied by the alpha value (see BLENDFUNCTION for reference).
If the alpha value is 255, the color channel values are already correct. If the alpha value is 0, all color channels need to be multiplied by zero (or simply set to 0). The translation doesn't produce any other alpha values.
I am using following code to convert BMP Image to GRF format.
Public Shared Function CreateGrf(filename As String, imagename As String) As String
Dim bmp As Bitmap = Nothing
Dim imgData As BitmapData = Nothing
Dim pixels As Byte()
Dim x As Integer, y As Integer, width As Integer
Dim sb As StringBuilder
Dim ptr As IntPtr
Try
bmp = New Bitmap(filename)
imgData = bmp.LockBits(New System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat)
width = (bmp.Width + 7) \ 8
pixels = New Byte(width - 1) {}
sb = New StringBuilder(width * bmp.Height * 2)
sb.Append(Environment.NewLine)
ptr = imgData.Scan0
For y = 0 To bmp.Height - 1
Marshal.Copy(ptr, pixels, 0, width)
For x = 0 To width - 1
sb.AppendFormat("{0:X2}", CByte(Not pixels(x)))
Next
sb.Append(Environment.NewLine)
ptr = ptr.ToInt64() + imgData.Stride
Next
Finally
If bmp IsNot Nothing Then
If imgData IsNot Nothing Then
bmp.UnlockBits(imgData)
End If
bmp.Dispose()
End If
End Try
Return [String].Format("~DG{0},{1},{2},", imagename, width * y, width) + sb.ToString()
End Function
However there is an extra vertical line drawn at the end of the converted GRF file even though there is no such line in the BMP file. Other than that the size and everything is Ok. It seems the last pixel (hex value) of each row is not correct in the GRF file.
Original BMP File.
Converted GRF FIle
Public Function ConvertBmp2Grf(fileName As String, imageName As String) As Boolean
Dim TI As String
Dim i As Short
Dim WID As Object
Dim high As Object
Dim TEM As Short, BMPL As Short, EFG As Short, n2 As String, LON As String
Dim header_name As String, a As String, j As Short, COUN As Short, BASE1 As Short
Dim L As String, TOT As String
Dim N As Object
Dim TOT1 As Integer
Dim LL As Byte
FileOpen(1, fileName, OpenMode.Binary, , , 1) ' OPEN BMP FILE TO READ
FileGet(1, LL, 1)
TI = Convert.ToString(Chr(LL))
FileGet(1, LL, 2)
TI += Convert.ToString(Chr(LL))
If TI <> "BM" Then
FileClose()
Return False
End If
i = 17
FileGet(1, LL, i + 1)
N = LL * 256
FileGet(1, LL, i)
N = (N + LL) * 256
FileGet(1, LL, i + 3)
N = (N + LL) * 256
FileGet(1, LL, i + 2)
N += LL
WID = N
i = 21
FileGet(1, LL, i + 1)
N = LL * 256
FileGet(1, LL, i)
N = (N + LL) * 256
FileGet(1, LL, i + 3)
N = (N + LL) * 256
FileGet(1, LL, i + 2)
N += LL
high = N
FileGet(1, LL, 27)
N = LL
FileGet(1, LL, 29)
If N <> 1 Or LL <> 1 Then
'BMP has too many colors, only support monochrome images
FileClose(1)
Return False
End If
TEM = Int(WID / 8)
If (WID Mod 8) <> 0 Then
TEM += 1
End If
BMPL = TEM
If (BMPL Mod 4) <> 0 Then
BMPL += (4 - (BMPL Mod 4))
EFG = 1
End If
n2 = fileName.Substring(0, fileName.LastIndexOf("\", StringComparison.Ordinal) + 1) + imageName + ".GRF"
FileOpen(2, n2, OpenMode.Output) 'OPEN GRF TO OUTPUT
TOT1 = TEM * high : TOT = Mid(Str(TOT1), 2)
If Len(TOT) < 5 Then
TOT = Strings.Left("00000", 5 - Len(TOT)) + TOT
End If
LON = Mid(Str(TEM), 2)
If Len(LON) < 3 Then
LON = Strings.Left("000", 3 - Len(LON)) + LON
End If
header_name = imageName
PrintLine(2, "~DG" & header_name & "," & TOT & "," & LON & ",")
For i = high To 1 Step -1
a = ""
For j = 1 To TEM
COUN = 62 + (i - 1) * BMPL + j
FileGet(1, LL, COUN)
L = LL
If j = TEM And (EFG = 1 Or (WID Mod 8) <> 0) Then
BASE1 = 2 ^ ((TEM * 8 - WID) Mod 8)
L = Int(L / BASE1) * BASE1 + BASE1 - 1
End If
L = Not L
a += Right(Hex(L), 2)
Next j
PrintLine(2, a)
Next i
FileClose()
Return True
End Function
Marshal.Copy(ptr, pixels, 0, width)
The Bitmap is not byte aligned. So in this case when you copy the data in it is filling in the left over bits as black.
the bitmap is 154 bytes wide which creates 19 full bytes and 2 left over pixels. So the remaining 6 pixels are black.
In the end you need to use bitmaps with widths that are divisible by eight or make sure the end of the data copy from the bitmap to pixels(x) accounts for the remaining bytes.
1) remove "7" in this part : width = (bmp.Width + 7) \ 8
2) detect if the bitmap's remaining value after Mod
if(bmp.Width % 8 > 0)
{
var remaining = bmp.Width % 8;
var newbmp = ResizeImage(bmp, bmp.Width + remaining, bmp.Height);
bmp.Dispose();
bmp = newbmp;
}
the logic for ResizeImage
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var oldRect = new Rectangle(0, 0, image.Width, image.Height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.FillRectangle(Brushes.White, destRect);
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, oldRect, 0, 0, image.Width, image.Height,
GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
I am trying to implement my own Thinning Algorithm in Matlab to understand the thinning algorithm. I am following http://fourier.eng.hmc.edu/e161/lectures/morphology/node2.html and implementing my own code, but the result is incorrect.
Here is my code:
%for the sake of simplicity, the outermost pixels are ignored.
for x = 2:1:511
for y = 2:1:511
% if this pixel is not black, then, proceed in.
if (frame2(y,x) > 0)
% the pos(1 to 8) here are for the surrounding pixels.
pos(1) = frame2(y-1,x-1);
pos(2) = frame2(y, x-1);
pos(3) = frame2(y+1, x+1);
pos(4) = frame2(y+1, x);
pos(5) = frame2(y+1, x-1);
pos(6) = frame2(y, x-1);
pos(7) = frame2(y-1, x-1);
pos(8) = frame2(y-1, x);
nonZeroNeighbor = 0;
transitSequence = 0;
change = 0;
for n = 1:1:8
% for N(P1)
if (pos(n) >= 1)
nonZeroNeighbor = nonZeroNeighbor + 1;
end
% for S(P1)
if (n > 1)
if (pos(n) ~= change)
change = pos(n);
transitSequence = transitSequence + 1;
end
else
change = pos(n);
end
end
% also for S(P1)
if ((nonZeroNeighbor > 1 && nonZeroNeighbor < 7) || transitSequence >= 2)
markMatrix(y,x) = 1;
fprintf(1, '(%d,%d) nonzero: %d transit: %d\n', y,x, nonZeroNeighbor, transitSequence);
else %this else here is for the reverse.
end
end
end
end
for x = 2:1:511
for y = 2:1:511
if (markMatrix(y,x) > 0)
frame2(y,x) = 0;
end
end
end
savePath = [path header number2 '.bmp'];
imwrite(frame2, savePath, 'bmp'); %output image here, replacing the original
From the site above, it states the function S(P1) as:
"S(P1): number of 0 to 1 (or 1 to 0) transitions in the sequence (P2, P3, ..., P9)"
For this part, my codes are below "% for S(P1)" and "% also for S(P1)" comments. Am I implementing this function correctly? The output image I got is simply blank. Nothing at all.
For the correct output, I am aware that there is a logical problem. Regarding the site, it states:
When part of the shape is only 2-pixel wide, all pixels are boundary points and will be marked and then deleted.
This problem is to be ignored for now.
I've had a go at the problem and think I managed to get the algorithm to work. I've made several small edits along the way (please see the code below for details), but also found two fundamental problems with your initial implementation.
Firstly, you assumed all would be done in the first pass of step 1 and 2, but really you need to let the algorithm work away at the image for some time. This is typical for iterative morphological steps 'eating' away at the image. This is the reason for the added while loop.
Secondly, your way of calculating S() was wrong; it counted both steps from 0 to 1 and 1 to 0, counting twice when it shouldn't and it didn't take care of the symmetry around P(2) and P(9).
My code:
%Preliminary setups
close all; clear all;
set(0,'DefaultFigureWindowStyle','Docked')
%Read image
frame2 = imread('q1.jpg');
%Code for spesific images
%frame2(:,200:end) = [];
%frame2 = rgb2gray(frame2);
%Make binary
frame2(frame2 < 128) = 1;
frame2(frame2 >= 128) = 0;
%Get sizes and set up mark
[Yn Xn] = size(frame2);
markMatrix = zeros(Yn,Xn);
%First visualization
figure();imagesc(frame2);colormap(gray)
%%
%While loop control
cc = 0;
changed = 1;
while changed && cc < 50;
changed = 0;
cc = cc + 1;
markMatrix = zeros(Yn,Xn);
for x = 2:1:Xn-1
for y = 2:1:Yn-1
% if this pixel is not black, then, proceed in.
if (frame2(y,x) > 0)
% the pos(2 to 9) here are for the surrounding pixels.
pos(1) = frame2(y, x);
pos(2) = frame2(y-1, x);
pos(3) = frame2(y-1, x+1);
pos(4) = frame2(y, x+1);
pos(5) = frame2(y+1, x+1);
pos(6) = frame2(y+1, x);
pos(7) = frame2(y+1, x-1);
pos(8) = frame2(y, x-1);
pos(9) = frame2(y-1, x-1);
nonZeroNeighbor = 0;
transitSequence = 0;
change = pos(9);
for n = 2:1:9
%N()
nonZeroNeighbor = sum(pos(2:end));
%S()
if (double(pos(n)) - double(change)) < 0
transitSequence = transitSequence + 1;
end
change = pos(n);
end
%Test if pixel is to be removed
if ~( nonZeroNeighbor == 0 || nonZeroNeighbor == 1 ...
||nonZeroNeighbor == 7 || nonZeroNeighbor == 8 ...
||transitSequence >= 2)
markMatrix(y,x) = 1;
fprintf(1, '(%d,%d) nonzero: %d transit: %d\n', ...
y,x, nonZeroNeighbor, transitSequence);
end
end
end
end
%Mask out all pixels found to be deleted
frame2(markMatrix > 0) = 0;
%Check if anything has changed
if sum(markMatrix(:)) > 0;changed = 1;end
end
%Final visualization
figure();imagesc(frame2);colormap(gray)