Bitmap24 to text - transforme une image en texte vc++

Soyez le premier à donner votre avis sur cette source.

Vue 7 393 fois - Téléchargée 655 fois

Description

Ce code permet de tansformer un fichier bitmap 24 bits en un fichier texte (voir capture)
Le resultat n'est convaincant qu'avec certaines polices de caractère, en particulier il fait que la taille des caractères soit la même.
On a un bon rendu avec la police Lucida Console (celle par defaut du bloc note wnd), c'est la police pour laquelle j'ai choisi des valeurs de caractères plus ou moins sombres qui marchent.

Ce code permet d'illustrer la maniere de lire un fichier bmp 24bits et d'utiliser les boites de dialogue d'ouverture de fichier

Au niveau du code je crois qu'il est assez commenté

Source / Exemple :


/*-----------------------------------------------------------------------------------------------*
//
//                                      Bitmap24 to text
//
//Fait par         : MaegisInstinct (maegisinstinct@free.fr)
//
//le               : 19/03/2004 à 11:16:00
//
//Description      : Converti une image BMP24 bits en fichier texte
//					 Mettre en police Lucida Console pour un bon resultat !!
//						NEW *Gestion du scale
//
/*----------------------------------------------------------------------------------------------*/

#include <windows.h>
#include "resource.h"

BOOL CALLBACK DlgProc(HWND,UINT,WPARAM,LPARAM);

#define PARTDEC(a) (a-(double)(int)a)		//Partie decimale d'un double

//Structure de pixel
typedef struct 
{
	BYTE r;
	BYTE g;
	BYTE b;
}PIXEL;

/*----------------------------------*/
//Charge un fichier Bmp24 bits
//Et calcule la luminosité maximale
/*----------------------------------*/
BOOL GetBmp24(char* chemin,PIXEL **data,DWORD& width,DWORD& height,BYTE& lummax,BYTE &lummin)
{
	BITMAPFILEHEADER	fileheader;
	BITMAPINFOHEADER	infoheader;
	HANDLE				fichier;		//Handle du fichier
	BYTE				*buffer = NULL;	//Buffer qui contiendra l'image
	DWORD				dummy,i,j;
	DWORD				size;			//taille du fichier
	DWORD				bourrage = 0;	//bourrage (super important pour eviter les decalages)
	PIXEL				temp;

	fichier = CreateFile(chemin,
						 GENERIC_READ,
						 FILE_SHARE_READ,
						 NULL,
						 OPEN_EXISTING,
						 FILE_ATTRIBUTE_NORMAL,
						 NULL);
	if (fichier == INVALID_HANDLE_VALUE)
		return FALSE;

	ReadFile(fichier,&fileheader,14,&dummy,NULL);		//On lit le header
	ReadFile(fichier,&infoheader,40,&dummy,NULL);
	width = infoheader.biWidth;
	height = infoheader.biHeight;

	while ((3*width+bourrage) % 4 != 0)		//Il faut que le nb d'octets pris par une ligne 
		bourrage++;							//soit un multiple de 4

	if (fileheader.bfType != 0x4D42 || infoheader.biBitCount != 24
		|| infoheader.biCompression != 0)	//fichier bmp24 bits sans compression
	{
		CloseHandle(fichier);
		return FALSE;
	}
	size = width*height;

	buffer = new BYTE[size*3+bourrage*height];
	if (buffer == NULL)
		return FALSE;

	SetFilePointer(fichier,fileheader.bfOffBits,NULL,FILE_BEGIN);

	ReadFile(fichier,buffer,size*3+bourrage*height,&dummy,NULL);	//on lit le fichier

	(*data) = new PIXEL[size];
	if ((*data) == NULL)
	{
		delete[] buffer;
		return FALSE;
	}

	for(i=0;i<height;i++)		//on copie les données
	{
		memcpy((*data)+(i*width),buffer+i*(3*width+bourrage),width*3);
	}

	//On remet l'image dans le bon sens (elle est stockée a l'envers)
	/*Et on calcule la luminositée maximale et minimale dans le fichier, ce qui va
	//servir a determiner quels caracteres on va utiliser
	//On defini la luminosité comme la moyenne de la valeur des composantes RBG du pixel*/
	for (j=0;j<height/2;j++)
	{
		for (i=0;i<width;i++)
		{
			temp  = (*data)[i+j*width];

			if ((temp.b+temp.g+temp.r)/3 > lummax)
				lummax = (temp.b+temp.g+temp.r)/3;
			if ((temp.b+temp.g+temp.r)/3 < lummin)
				lummin = (temp.b+temp.g+temp.r)/3;

			(*data)[i+j*width] = (*data)[i+(height-1-j)*width];

			if (((*data)[i+j*width].b+(*data)[i+j*width].g+(*data)[i+j*width].r)/3 > lummax)
				lummax = ((*data)[i+j*width].b+(*data)[i+j*width].g+(*data)[i+j*width].r)/3;
			if (((*data)[i+j*width].b+(*data)[i+j*width].g+(*data)[i+j*width].r)/3 < lummin)
				lummin = ((*data)[i+j*width].b+(*data)[i+j*width].g+(*data)[i+j*width].r)/3;

			(*data)[i+(height-1-j)*width] = temp;
		}
	}
	
	CloseHandle(fichier);
	delete[] buffer;
	return TRUE;
}

/*-----------------------------------------*/
//Enregistre l'image dans le fichier texte
/*-----------------------------------------*/
BOOL SaveText(char* chemin,PIXEL* data,DWORD width,DWORD height,BYTE lummax,BYTE lummin)
{
	HANDLE	fichier;			//Handle du fichier texte
	DWORD	i,j;
	DWORD	cur = 0;
	DWORD	size;
	BYTE	luminosite;			//luminosité du pixel courant
	char	*buffer;			
	char	text[7] ={' ',':','*','€','%','@','#'};		//Caracters du plus clair au plus
														//sombre (Lucida Console)
	double	div = (double)(lummax-lummin)/6.0;			//coeef pour le calcul du char

	size	= width*height+(height-1)*2;
	buffer	= new char[size];
	if (buffer == NULL)
		return FALSE;

	//On remplit le buffer 
	for(j=0;j<height;j++)
	{
		for(i=0;i<width;i++)
		{
			luminosite = (data[j*width+i].r+data[j*width+i].g+data[j*width+i].b)/3;
			buffer[cur] = text[(int)((lummax-luminosite+lummin)/div)];
			cur++;
		}

		if(j!=height-1)
		{
			buffer[cur] = 13;
			buffer[cur+1] = 10;
			cur += 2;
		}
	}

	fichier  = CreateFile(chemin,
						  GENERIC_WRITE,
						  FILE_SHARE_READ,
						  NULL,
						  CREATE_ALWAYS,
						  FILE_ATTRIBUTE_NORMAL,
						  NULL);
	if (fichier != INVALID_HANDLE_VALUE)
	{
		WriteFile(fichier,buffer,size,&cur,NULL);
		CloseHandle(fichier);
		delete[] buffer;
		return TRUE;
	}
	delete[] buffer;
	return FALSE;
}

/*---------------------------------*/
//Permet d'étirer/rétrecir l'image
/*---------------------------------*/
void Scale(const PIXEL* src,DWORD srcwidth,DWORD srcheight,PIXEL* data,DWORD datawidth,DWORD dataheight,BOOL moyenne_etirement)
{
	DWORD	i,j,k,l;
	PIXEL*	save_data; 
	double	xratio,yratio;
	double	precx = 0.0,precy = 0.0;
	double	currentx,currenty;
	double	coeff;						//somme des coeffs pour la moyenne
	double	temp;
	double	somme_r,somme_g,somme_b;

	xratio = (double)srcwidth/(double)datawidth;
	yratio = (double)srcheight/(double)dataheight;
	save_data = data;
	currenty = 0.0;

	for(j=0;j<dataheight;j++)			//Pour chaque pixel du but
	{
		currentx = 0.0;
		currenty += yratio;
		for(i=0;i<datawidth;i++)
		{
			currentx += xratio;
			coeff = 0.0;
			somme_r = 0.0;
			somme_g = 0.0;
			somme_b = 0.0;
		
			//Calcul de la moyenne
			for(l=0;l<(unsigned int)yratio+1;l++)		//Pour chaque pixel de la source contenu dans le pixel du but
			{
				for(k=0;k<(unsigned int)xratio+1;k++)
				{
				//Coeff en x
					if (k==0)
						temp = 1.0-PARTDEC(precx);
					else if (k==(unsigned int)xratio)
						temp = xratio-(double)((int)xratio-1)-(1.0-PARTDEC(precx));
					else
						temp = 1;
				//Coeff en y
					if (l==0)
						temp = temp*(1.0-PARTDEC(precy));
					else if (l==(unsigned int)yratio)
						temp = temp*(yratio-(double)((int)yratio-1)-(1.0-PARTDEC(precy)));

					if ((int)precx+k<srcwidth && (int)precy+l<srcheight)		//si on est dans les limites
					{
						somme_r += src[(int)precx+k+((int)precy+l)*srcwidth].r*temp;
						somme_g += src[(int)precx+k+((int)precy+l)*srcwidth].g*temp;
						somme_b += src[(int)precx+k+((int)precy+l)*srcwidth].b*temp;
						coeff += temp;
					}

					if (moyenne_etirement)  //Moyenne lors de l'etirement si souhaité
					{
						if (xratio<1. && (int)currentx-(int)precx==1 && PARTDEC(currentx) != 0.)
						{
							temp = PARTDEC(currentx);
							somme_r += src[(int)precx+1 + (int)precy*srcwidth].r*temp;
							somme_g += src[(int)precx+1 + (int)precy*srcwidth].g*temp;
							somme_b += src[(int)precx+1 + (int)precy*srcwidth].b*temp;
							coeff += temp;
							temp = -1.0;
						}
						if (yratio<1. && (int)currenty-(int)precy==1 && PARTDEC(currenty) != 0.)
						{
							if (temp == -1.0)		//on compte le quatrieme carré
							{
								temp = PARTDEC(currenty)*PARTDEC(currentx);
								somme_r += src[(int)precx+1 + ((int)(precy)+1)*srcwidth].r*temp;
								somme_g += src[(int)precx+1 + ((int)(precy)+1)*srcwidth].g*temp;
								somme_b += src[(int)precx+1 + ((int)(precy)+1)*srcwidth].b*temp;
								coeff += temp;
							}
							temp = PARTDEC(currenty);
							somme_r += src[(int)precx + ((int)(precy)+1)*srcwidth].r*temp;
							somme_g += src[(int)precx + ((int)(precy)+1)*srcwidth].g*temp;
							somme_b += src[(int)precx + ((int)(precy)+1)*srcwidth].b*temp;
							coeff += temp;
						}
					}
				}
			}
			data->r = (BYTE)(somme_r/coeff);
			data->g = (BYTE)(somme_g/coeff);
			data->b = (BYTE)(somme_b/coeff);
			data++;
			precx = currentx;
		}
		precy = currenty;
		precx = 0.0;
	}
	data = save_data;
}

/*--------------*/
//Fonction Main
/*--------------*/
int APIENTRY WinMain(HINSTANCE hInstance,
					 HINSTANCE hPrevInstance,
					 LPSTR lpCmdLine,
					 int nShowCmd)
{
	HWND hWndDlg;
	MSG msg;
	
	hWndDlg = CreateDialog(hInstance,MAKEINTRESOURCE(IDD_DIALOG),NULL,&DlgProc);//Crée la boite de
																				//dialogue
	ShowWindow(hWndDlg,SW_SHOW);	//Affiche la bdd

	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);				//Boucle de messages
		DispatchMessage(&msg);
	}
	return 0;
}

/*----------------------------*/
//Fonction callbaxk de la BDD
/*----------------------------*/
BOOL CALLBACK DlgProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	int bouton;					//en cas de WM_COMMAND

	switch(uMsg)
	{
	case WM_SYSCOMMAND :				//si on clique sur la croix
		if (wParam == SC_CLOSE)			//on ferme la fenetre
			PostQuitMessage(0);
		return TRUE;
	case WM_INITDIALOG :			//a la création de la boite de dialogue
		SetDlgItemText(hWnd,IDC_FICHIERSOURCE,"Aucun fichier selectionné");
		SetDlgItemText(hWnd,IDC_FICHIERDEST,"Aucun fichier selectionné");
		return TRUE;

	case WM_COMMAND :

		bouton = LOWORD(wParam);		//Le bouton envoyant le message

		switch(bouton)		
		{
		case IDC_QUIT :				//Si c'est quit on quitte
			PostQuitMessage(0);
			return TRUE;
		case IDC_SOURCE :			//Si c'est source

			char cheminsource[_MAX_FNAME];	//va contenir le chemin du fichier source
			OPENFILENAME ofn;				//structure pour la bdg ouvrir

			memset(&ofn,0,sizeof(OPENFILENAME));		//on la set à 0
			ofn.lStructSize = sizeof(OPENFILENAME);
			ofn.hwndOwner = hWnd;				//Handle de la fenetre parente ou NULL
			ofn.hInstance = NULL;
			ofn.lpstrFilter = "Fichier BMP 24bits (*.bmp)\0*.bmp\0";
			ofn.lpstrCustomFilter = NULL;
			ofn.nMaxCustFilter = NULL;
			ofn.nFilterIndex = 0;				//index du filtre par defaut
			ofn.lpstrFile = cheminsource;		//votre buffer de fichier
			ofn.nMaxFile = _MAX_FNAME;
			ofn.lpstrFileTitle = NULL;		//buffer contenant le nom du fichier.ext
			ofn.lpstrInitialDir = NULL;		//dir initial;
			ofn.lpstrTitle = "Selectionnez le bitmap";	//titre de la BDG
			ofn.Flags = OFN_FILEMUSTEXIST;	//Selectionner seulement un fichier qui existe 
			ofn.lCustData = NULL;
			ofn.lpfnHook = NULL;
			ofn.lpTemplateName = NULL;
			cheminsource[0] = 0;

			if(GetOpenFileName(&ofn) != 0)		//Si pas de probleme
			{
				SetDlgItemText(hWnd,IDC_FICHIERSOURCE,cheminsource);	//mettre le nom du 
																//fichier dans la edit box
			}
			return TRUE;
		case IDC_DEST :			//Si c'est le bouton de selection du fichier de dest

			char chemindest[_MAX_FNAME];	//va contenir le chemin du fichier destination
	
			chemindest[0] = 0;				

			OPENFILENAME ofn2;				//structure pour la bdg ouvrir

			memset(&ofn2,0,sizeof(OPENFILENAME));		//on la set à 0
			ofn2.lStructSize = sizeof(OPENFILENAME);
			ofn2.hwndOwner = hWnd;				//Handle de la fenetre parente ou NULL
			ofn2.hInstance = NULL;
			ofn2.lpstrFilter = "Fichier txt (*.txt)\0*.txt\0";
			ofn2.lpstrCustomFilter = NULL;
			ofn2.nMaxCustFilter = NULL;
			ofn2.nFilterIndex = 0;				//index du filtre par defaut
			ofn2.lpstrFile = chemindest;		//votre buffer de fichier
			ofn2.nMaxFile = _MAX_FNAME;
			ofn2.lpstrFileTitle = NULL;	//buffer contenant le nom du fichier.ext
			ofn2.lpstrInitialDir = NULL;		//dir initial;
			ofn2.lpstrTitle = "Selectionnez le fichier destination";	//titre de la BDG
			ofn2.Flags = OFN_PATHMUSTEXIST;
			ofn2.lCustData = NULL;
			ofn2.lpfnHook = NULL;
			ofn2.lpTemplateName = NULL;

			if(GetSaveFileName(&ofn2) != 0)		//Si pas de probleme
				SetDlgItemText(hWnd,IDC_FICHIERDEST,chemindest);	//mettre le nom du 
																	//fichier dans la edit box
			return TRUE;

		case IDC_PROCESS:  //on veut crypter

			char pathdest[_MAX_FNAME];	//chemin du fichier de dest
			char pathsrc[_MAX_FNAME];
			DWORD height,width;
			BYTE lummax,lummin;
			PIXEL *data = NULL;
			PIXEL *scaleddata;
			int xresol,yresol;

			GetDlgItemText(hWnd,IDC_FICHIERDEST,pathdest,_MAX_FNAME);		//obtient les paths
			GetDlgItemText(hWnd,IDC_FICHIERSOURCE,pathsrc,_MAX_FNAME);

			if (strcmp(pathsrc,"Aucun fichier selectionné") == 0)		//si pas fichier src
			{
				MessageBox(hWnd,"Selectionne d'abord le fichier source!","Erreur fichier",MB_OK | MB_ICONEXCLAMATION);
				return TRUE;
			}

			if (strcmp(pathdest,"Aucun fichier selectionné") == 0)		//si pas fichier dest
			{
				MessageBox(hWnd,"Selectionne d'abord le fichier de destination!","Erreur fichier",MB_OK | MB_ICONEXCLAMATION);
				return TRUE;
			}

			xresol = GetDlgItemInt(hWnd,IDC_X,NULL,FALSE);
			yresol = GetDlgItemInt(hWnd,IDC_Y,NULL,FALSE);

			if(GetBmp24(pathsrc,&data,width,height,lummax,lummin))
			{
				if (xresol != 0 && yresol !=0)		//Gestion du scale
				{
					scaleddata = new PIXEL[xresol*yresol];
					if (scaleddata != NULL)
					{
						Scale(data,width,height,scaleddata,xresol,yresol,FALSE);
						delete[] data;
						data	= scaleddata;
						width	= xresol;
						height	= yresol;
					}
				}

				if(SaveText(pathdest,data,width,height,lummax,lummin))
					MessageBox(hWnd,"Opération réussie","Bitmap24ToText",MB_OK | MB_ICONEXCLAMATION);
				else
					MessageBox(hWnd,"Erreur à l'écriture du fichier de destination","Bitmap24ToText",MB_OK | MB_ICONEXCLAMATION);
		
				delete[] data;
			}
			else
				MessageBox(hWnd,"Erreur a l'ouverture du fichier source\nVerifiez que le fichier soit bien un bitmap 24bits","Bitmap24ToText",MB_OK | MB_ICONEXCLAMATION);

			return TRUE;	
		}
		return FALSE;
	}
	return FALSE;
}

Conclusion :


Une chose importante a noter dans la lecture du fichier Bmp : la gestion des octects de bourrage !! Si on en tient pas compte l'image sera toute décalée

Attention, choisir une taille de police adaptée à la résolution (Format police) pour bien voir l'image et désactiver le retour a la ligne automatique !!

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

cs_JCDjcd
Messages postés
1138
Date d'inscription
mardi 10 juin 2003
Statut
Membre
Dernière intervention
25 janvier 2009
2 -
Bravo de chez bravo. J'admire l'oeuvre. C'est bien fais et bien sur ca marche sur bien sur des exemples compliques.

Bon sinon je n'ai pas compris comment tu fais pour choisir quel caractere pendre ??
MoDDiB
Messages postés
546
Date d'inscription
mardi 26 novembre 2002
Statut
Membre
Dernière intervention
4 mai 2007
1 -
Umm d'apres ce que je viens de voir il a classé les caractere a mettre du plus clair au plus sombre et par rapport a la luminosité du pixel coutrant il le remplace ! astucieux ^^
ymca2003
Messages postés
2070
Date d'inscription
mardi 22 avril 2003
Statut
Membre
Dernière intervention
3 juillet 2006
8 -
super cette source, ça sort de l'ordinaire.
cs_LordBob
Messages postés
2865
Date d'inscription
samedi 2 novembre 2002
Statut
Membre
Dernière intervention
11 mai 2009
8 -
c'est clair c'est excelent !!!
10/10
Helkanen
Messages postés
54
Date d'inscription
mardi 24 décembre 2002
Statut
Membre
Dernière intervention
9 juin 2004
-
Je me souviens qu'un mec (le pote d'un pote pour être hyper-précis) avait fait un projet dans le même genre, très évolué tout comme il faut...
je vous conseille vivement d'aller faire un tour sur portail.babeuk.net (dans Art ASCII)
seulement j'ai pas réussi à trouver les sources de la DLL (je sais pas si il a fait ca en open source)
quoi qu'il en soit le principe est vraiment sympa...

Je me demande juste si y'a pas moyen de créer qqch de super performant qui permettrait l'utilisation de toutes les polices de caractères, en utilisant un HDC virtuel, en écrivant chaque caractère un à un puis tester sa luminosité (nombre de pixels allumés sur nombre total), et faire avec ça un tableau des luminosités des caractères...

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.