ARQUIVO COM FORMATO PROPRIET?RIO
Preciso ler uma seqüência de imagens que estão armazenadas em um arquivo de formato próprio, criado e mantido por outro aplicativo.
Esse arquivo, com extensão [Ô].iset[Ô], possui codificação UTF-8 e gravação dos dados é em formato binário, [Ô]little-endian[Ô].
Em tese, seguindo o layout que me foi informado, tudo ocorre bem até o ponto em que a rotina lê (em uma matriz) os bytes relativos à primeira imagem.
No momento em que tento gerar um novo Bitmap à partir de um MemoryStream baseado naquela matriz de bytes especÃfica, ocorre um erro, sendo que a mensagem de erro é apenas [Ô]Parâmetros Inválidos[Ô].
A definição de layout desse arquivo é, conforme seu criador, a seguinte:
Citação:...
1. Atualmente o header do arquivo de ImageSet tem o seguinte formato:
· 4 bytes com a assinatura 0x74657366, para confirmar que é valido.
· 4 bytes com a versão do arquivo (hoje é 0x01000000, e quando mudar o layout, eu altero esse valor).
· 4 bytes com o contador de blocos.
2. Depois desse header há um bloco por imagem, com o seguinte layout:
· 4 bytes com o tamanho da imagem.
· Imagem (tipicamente JPEG, mas eventualmente pode conter BMP).
· 4 bytes com o tamanho do template.
· Template (com tamanhos e composições variados, apenas desconsidere este campo).
· 4 bytes com o timestamp da imagem (segundos desde 01/Jan/1970). Esse campo codifica diretamente o valor, sem o prefixo de 4 bytes que aparece nos campos anteriores.
· Um array de floating points (8 bytes cada) contendo o score de cada imagem. O comprimento desse array é o mesmo definido pelo contador que aparece no cabeçalho do arquivo, e como o tamanho é bem conhecido, também dispensa o prefixo de 4 bytes.
· 4 bytes com o tamanho de eventuais comentários.
· Comentário opcional.
3. Cada um desses blocos é prefixado de um inteiro de 4 bytes contendo o tamanho Do bloco todo, e eles são armazenados em sequencia no arquivo.
...
Alguém poderia me dar um auxÃlio ?
O trecho de código que tenta fazer o trabalho é o seguinte:
...
Try
If IO.File.Exists(arquivo) = True Then
[ô] Formato
Dim oFmt As Encoding = Nothing
[ô] Nome:
Dim oFI As FileInfo = New FileInfo(arquivo)
ret.Arquivo = oFI.Name.Replace(oFI.Extension, [Ô][Ô])
[ô] Encoding:
Using sr = My.Computer.FileSystem.OpenTextFileReader(arquivo)
ret.Encoding = sr.CurrentEncoding
oFmt = ret.Encoding
sr.Close()
End Using
[ô] Cabeçalho:
Using oFS As New FileStream(arquivo, FileMode.Open, FileAccess.Read)
Using oBR As New BinaryReader(oFS)
[ô] Variável para a carga dos bytes.
Dim bQQ As Byte()
[ô] Assinatura:
bQQ = ReadB(oBR, 4) [ô] oBR.ReadBytes(4)
ret.Assinatura = oFmt.GetString(bQQ)
[ô] Versão:
bQQ = ReadB(oBR, 4)
ret.Versao = String.Format([Ô]{0:00}.{1:00}.{2:00}.{3:00}[Ô], bQQ(0), bQQ(1), bQQ(2), bQQ(3))
[ô] Quantidade de faces:
bQQ = ReadB(oBR, 4)
ret.QuantidadeDeFaces = Convert.ToInt64(String.Format([Ô]{0:00}{1:00}{2:00}{3:00}[Ô], bQQ(0), bQQ(1), bQQ(2), bQQ(3)))
[ô] Leitura das faces:
For iFace As Integer = 0 To ret.QuantidadeDeFaces - 1
[ô] Nova face:
Dim fc As New Face With {.Index = (iFace + 1)}
[ô] Tamanho dos blocos de faces
bQQ = ReadB(oBR, 4)
fc.TamanhoDoBloco = Convert.ToInt64(String.Format([Ô]{0:00}{1:00}{2:00}{3:00}[Ô], bQQ(0), bQQ(1), bQQ(2), bQQ(3)))
[ô]Tamanho da imagem:
bQQ = ReadB(oBR, 4)
fc.TamanhoDaImagem = Convert.ToInt64(String.Format([Ô]{0:00}{1:00}{2:00}{3:00}[Ô], bQQ(0), bQQ(1), bQQ(2), bQQ(3)))
[ô] Imagem e bytes:
fc.ImagemEmBytes = ReadB(oBR, fc.TamanhoDaImagem)
[ô] Imagem:
Using ms As MemoryStream = New MemoryStream(fc.ImagemEmBytes)
ms.Position = 0
ms.Seek(0, SeekOrigin.Begin)
Try
Using bmp As Bitmap = Image.FromStream(ms, False, False)
fc.Imagem = bmp.Clone
End Using
Catch ex As Exception
fc.Imagem = New Bitmap(1, 1)
End Try
End Using
[ô] ...
[ô] outros campos
[ô] ...
Next
oBR.Close()
End Using
oFS.Close()
End Using
End If
Catch ex As Exception
ret = Nothing
End Try
...
E a função ReadB, bem simples e que em teoria faz a leitura dos bytes usando a [Ô]little endian[Ô] é a seguinte:
Private Shared Function ReadB(ByRef oBR As BinaryReader, lenght As Integer) As Byte()
Dim b As Byte() = Nothing
b = oBR.ReadBytes(lenght) [ô].Reverse.ToArray
If BitConverter.IsLittleEndian Then Array.Reverse(b)
Return b
End Function
Grato !
Esse trecho da documentação que você postou está meio confusa para mim, eu não sei se os valores que ela mostra como 0x74657366 e 0x01000000 é realmente um valor em hexadecimal ou é os bytes no arquivo, eu diria para você abrir o arquivo em um editor hexadecimal para conferir, pois veja que se você tiver em um arquivo os bytes em ordem
01 00 00 00
Quando você ler eles como se fosse um inteiro de 32 bits na verdade o seu valor seria o equivalente de escrever no VB
Dim i as Integer = 0x00000001
Pois em memória, e por consequência nos arquivos gravados os bytes ficam em little endian, onde o byte menos significativo é gravado primeiro, porém a representação em hexadecimal quando estamos programando usa o formato que faz mais sentido para a gente, onde o digito mais significativo é vem primeiro, e isso é o que mais confunde quando se começa a ver falar de little endian.
Então eu diria que onde a documentação fala para ler um valor de 4 bytes você pode simplesmente usar o ReadInt32() do BinaryReader, e na hora de ler a imagem simplesmente chame o ReadBytes(tamanho) do BinaryReader
Pois é, ocorre que para algumas informações (por exemplo, a assinatura) o Big Endian é usado, e em outras, o Little Endian. O que está errado na ReadB é checar LittleEndian na BitConverter. esse booleano deveria vir como parâmetro.
O problema não estava no código: Eu estava lendo arquivos da versão mais recente com essa estrutura de leitura, que usa uma das versões iniciais do padrão.
Bastou ler os arquivos corretos, e tudo funcionou perfeitamente.