/*******************************************************************\ Fichier : StenoseNBBase.cpp Date : 4/01/10 Version : 1.000 Description : classe de calcul d'une sténose N&B |*******************************************************************| Bugs: Notes: |*******************************************************************| Historique : 10/02/98 1.000 : Première version \*******************************************************************/ /*----------------------------------------------------------\ Includes \----------------------------------------------------------*/ #include #include "StenoseNBBase.h" #include "Ressource.h" //#include "../IMT/MeanEstimate.h" #include "../Object/regionEllipse.h" #include "../Object/point.h" #include "../NR/ToolsMath.h" #include "../Container/extendedimage.h" #include "../TRIM/img.h" #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif CStenoseNBBase::CStenoseNBBase (void) { // Couleur des points à afficher pour le degré de sténose m_rcStenose.SetRectEmpty(); m_step = stepNone; m_clrVert = 0 | (255 << 16) | (0 << 8); m_clrOrange = 216 | (216 << 16) | (28 << 8); m_pPoints = NULL; m_SeuilBlobColoring = 40; } CStenoseNBBase::~CStenoseNBBase() { Release (); } void CStenoseNBBase::Release() { if (m_pPoints) { delete [] m_pPoints; m_pPoints = NULL; } m_dwPoints = 0; // requis par la routine de mesure } double CStenoseNBBase::Ratio() { return (100.0 * m_dwOther / (m_dwGreen + m_dwOther)); } double CStenoseNBBase::Surface() { return m_dblSurface; } int CStenoseNBBase::Density() { return m_dwMean; } bool CStenoseNBBase::ParametrerRegionATraiter (Rect &rcStenose, Point ptStenose) { // Région à traiter m_ptStenose = ptStenose; m_rcStenose = rcStenose; m_rgnATraiter.DeleteObject(); m_rgnATraiter.CreateEllipticRgn(rcStenose); return true; } /*----------------------------------------------------------\ | Mesurer | |-----------------------------------------------------------| | DESCRIPTION : | | On a saisi un segment. Mesurer l'épaisseur de droites | | perpendiculaires. | |-----------------------------------------------------------| | PARAMETRES : | | ... | \----------------------------------------------------------*/ void CStenoseNBBase::TraiterPoint (int x, int y) { assert(PointInBufferResult(x, y)); if (PointInBufferResult(x, y)) { int xx, yy, xMax, yMax; // Colorer ce point en vert // SetPixelResult(y, x, m_clrVert); SetPixelResult(x, y, m_clrOrange); // Pour optimiser le temps de calcul, on effectue les tests les plus rapides en premier xMax = min (x + 1, GetWidth() - 1); yMax = min (y + 1, GetHeight() - 1); for (xx = max (0, x - 1); xx <= xMax; xx++) { for (yy = max (0, y - 1); yy <= yMax; yy++) { if ((xx != x) || (yy != y)) { assert(PointInBufferResult(xx, yy)); // <= sur les intensités sinon à 255 tout n'est pas rempli if ( (GetIntensityResult(xx, yy) <= m_iMax) // && (GetPixelResult(xx, yy) != m_clrVert) && (GetPixelResult(xx, yy) != m_clrOrange) && m_rgnATraiter.PtInRegion0(xx, yy) ) { TraiterPoint (xx, yy); // Pas de max } } } } } } int CStenoseNBBase::GetIntensityTache(int ptCentrex, int ptCentrey, int nRayonTache) { int nIntensiteTache = 0; // Quelle est l'intensité monochrome de la tache autour de ce pt ? for (int iX = min (m_result->GetWidth()-1, max (0, ptCentrex - nRayonTache)); iX <= min (m_result->GetWidth()-1, ptCentrex + nRayonTache); iX++) { for (int iY = min (m_result->GetHeight()-1, max (0, ptCentrey - nRayonTache)); iY <= min (m_result->GetHeight()-1, ptCentrey + nRayonTache); iY++) { nIntensiteTache += m_result->GetPixelRGB( iX, iY, 0); } } return (nIntensiteTache / ((nRayonTache * 2 + 1) * (nRayonTache * 2 + 1))); } int CStenoseNBBase::Threshold_2(ExtendedImage *h_image, int iMax, Rect &rcEllipse, int ptx, int pty) { int dwColor; int retour; iMax = 0; // Unused m_result = h_image; m_wimg.Del(); m_wimg.Create(TYPE_UCHAR, GetWidth(), GetHeight()); m_wimg.Fill(0); Release(); m_ptStenose.x = ptx; m_ptStenose.y = pty; m_step = stepSettings; m_rcStenose = rcEllipse; m_rcStenose.right++; m_rcStenose.bottom++; // nombre maximal de points qui pourraient être affichés m_pPoints = new Point [m_rcStenose.Width() * m_rcStenose.Height()]; m_rgnATraiter.DeleteObject (); m_rgnATraiter.CreateEllipticRgn(m_rcStenose); // GetIntensityTache inclus dans la librairie m_iMax = GetIntensityTache(ptx, pty, 7) + 10; TraiterPoint(m_ptStenose.x, m_ptStenose.y); // Fermeture et Effacement des petites régions DoClosing(); DoBlobColoring(); //***** Afficher les résultats GraphMeanInit(); dwColor = 0; m_dwGreen = 0; m_dwOther = 0; m_dwMean = 0; m_dblVesselArea = g_pCurrentScale->Surface (m_rcStenose.Width(), m_rcStenose.Height()) * M_PI / 4; for (int x = m_rcStenose.left; x < m_rcStenose.right; x++) { for (int y = m_rcStenose.top; y < m_rcStenose.bottom; y++) { bool fGreen; // Même en dehors de la région sinon le bord inférieur droit est exclu // fGreen = (GetPixelResult(x, y) == m_clrVert); fGreen = (GetPixelResult(x, y) == m_clrOrange); if (fGreen) { m_dwGreen++; m_pPoints[m_dwPoints].x = x; m_pPoints[m_dwPoints].y = y; m_dwPoints++; } if (m_rgnATraiter.PtInRegion0(x, y)) { if (!fGreen) { dwColor += IsColor (x, y) ? 1 : 0; m_dwOther++; m_dwMean += GetIntensityResult(x, y); GraphMeanAddMeasure(true, GetIntensityResult(x, y)); } } } } if (m_dwOther) { m_dwMean /= m_dwOther; } assert(m_dwMean <= 255); m_dblSurface = m_dblVesselArea * Ratio () / 100.0; if (dwColor > 50) { retour = 10; } else { retour = 11; } return retour; } // fin de Threshold_2 // Algorithme de coloriage de blobs sur l'image d'entrée // Objectif : enlever les petites régions dans l'image seuillée bool CStenoseNBBase::DoBlobColoring() { int x, y, i, indblob, nblob, realblob, ng; long taille; img lwimg, lwimg1; int wdimh, wdimv; blob m_tblob[MAXBLOB]; wdimh = GetWidth(); wdimv = GetHeight(); lwimg.Create(TYPE_UCHAR, wdimh, wdimv); lwimg1.Create(TYPE_INT, wdimh, wdimv); // On prend des ints lwimg.Fill(0); indblob = 0; nblob = 0; // Générer l'image binaire qui va aller à l'algo. du blob coloring // Lwimg for (x = m_rcStenose.left; x < m_rcStenose.right; x++) { for (y = m_rcStenose.top; y < m_rcStenose.bottom; y++) { if (m_rgnATraiter.PtInRegion0(x, y)) { bool fGreen; fGreen = (GetPixelResult(x, y) == m_clrOrange); if (!fGreen) { lwimg.SetValue(x, y, 1); } } } } // Résultat : les blobs sont dans wimg1 nblob = lwimg.BlobColoring(m_tblob, &lwimg1); // Elimination des petits blobs. realblob = nblob; for (i = 1; i <= nblob; i++) { taille = lwimg1.GetBlobSize(i); if ((taille < (wdimh * wdimv) - 1) && (taille <= m_SeuilBlobColoring)) { lwimg1.RemplaceCouleur(i, MAXBLOB+1); // Sur int } } // Dans lwimg1, les pixels à la valeur MAXBLOB+1, sont ceux des régions à supprimer // Mise à jour de lwimg, puis transfert dans l'image d'origine for (x = m_rcStenose.left; x < m_rcStenose.right; x++) { for (y = m_rcStenose.top; y < m_rcStenose.bottom; y++) { if (m_rgnATraiter.PtInRegion0(x, y)) { ng = lwimg1.GetValue(x, y); if (ng == (MAXBLOB+1)) { SetPixelResult(x, y, m_clrOrange); } } } } if (lwimg.init == 1) lwimg.Del(); if (lwimg1.init == 1) lwimg1.Del(); return true; } // Algorithme de fermeture bool CStenoseNBBase::DoClosing() { int x, y, ng; img lwimg, lwimg1; int wdimh, wdimv; wdimh = GetWidth(); wdimv = GetHeight(); lwimg.Create(TYPE_USHORT, wdimh, wdimv); lwimg1.Create(TYPE_USHORT, wdimh, wdimv); // On prend des ints lwimg.Fill(0); // Générer l'image binaire qui va aller à l'algo. du blob coloring // lwimg for (x = m_rcStenose.left; x < m_rcStenose.right; x++) { for (y = m_rcStenose.top; y < m_rcStenose.bottom; y++) { if (m_rgnATraiter.PtInRegion0(x, y)) { bool fGreen; fGreen = (GetPixelResult(x, y) == m_clrOrange); if (fGreen) { lwimg.SetValue(x, y, 1); } } } } lwimg.BinaryClosing(&lwimg1); // Dans lwimg1, les pixels à la valeur MAXBLOB+1, sont ceux des régions à supprimer // Mise à jour de lwimg, puis transfert dans l'image d'origine for (x = m_rcStenose.left; x < m_rcStenose.right; x++) { for (y = m_rcStenose.top; y < m_rcStenose.bottom; y++) { if (m_rgnATraiter.PtInRegion0(x, y)) { ng = lwimg1.GetValue(x, y); if (ng == 1) { SetPixelResult(x, y, m_clrOrange); } } } } if (lwimg.init == 1) lwimg.Del(); if (lwimg1.init == 1) lwimg1.Del(); return true; } int CStenoseNBBase::GetWidth() { return m_result->GetWidth(); } int CStenoseNBBase::GetHeight() { return m_result->GetHeight(); } unsigned long CStenoseNBBase::GetPixelResult(const int& x, const int& y) { Point pt; pt.x = x; pt.y = y; return GetPixelResult(pt); } unsigned long CStenoseNBBase::GetPixelResult(const Point& pt) { unsigned long color; color = 0; if (m_wimg.GetValue(pt.x, pt.y) == 1) { color = m_clrOrange; } // m_wimg.SaveImgAsRaw(); return color; /* assert( m_result ); #if defined( WIN32 ) && !defined( PLAQUE_DLL ) return m_result->GetPixel( pt.x, pt.y ); #else return *m_result->GetPixel( pt.x, pt.y ); #endif */ } void CStenoseNBBase::SetPixelResult(Point& pt, unsigned long vColor) { int x, y; x = pt.x; y = pt.y; SetPixelResult(x, y, vColor); } void CStenoseNBBase::SetPixelResult(int x, int y, unsigned long vColor) { m_wimg.SetValue(x, y, 1); // m_wimg.SaveImgAsRaw(); } bool CStenoseNBBase::IsColor(int x, int y) { unsigned char bRed; unsigned char bGreen; unsigned char bBlue; int iRed; int iGreen; int iBlue; // AffectLeadResult(); bRed = m_result->GetPixelRGB( x, y, 0); bGreen = m_result->GetPixelRGB( x, y, 1); bBlue = m_result->GetPixelRGB( x, y, 2); iRed = (int) bRed; iGreen = (int) bGreen; iBlue = (int) bBlue; // un niveau de gris implique bRed = bGreen = bBlue // étant donné que l'image a été numérisé on autorise un delta de 10 entre chaque couleur // ex : 190 197 185 est gris, 190 201 190 est en couleur return ( (abs (iRed - iGreen) > 30) || (abs (iRed - iBlue ) > 30) || (abs (iGreen - iBlue ) > 30) ); } unsigned char CStenoseNBBase::GetIntensityResult(const int& x, const int& y) { Point pt; pt.x = x; pt.y = y; return GetIntensityResult(pt); } unsigned char CStenoseNBBase::GetIntensityResult(const Point& pt) { assert( m_result ); return m_result->GetPixelGray( pt.x, pt.y ); /* value = (char) m_result->GetPixel( pt.x, pt.y ); col = (unsigned char)(value & 0xff); return col; */ /* #if defined( WIN32 ) && !defined( IMT_DLL ) && !defined( PLAQUE_DLL ) return GetRValue( m_result->GetPixel( pt.x, pt.y ) ); #else return *m_result->GetPixel( pt.x, pt.y ); #endif */ } bool CStenoseNBBase::PointInBufferResult(const int& x, const int& y) { Point pt; pt.x = x; pt.y = y; return PointInBufferResult(pt); } bool CStenoseNBBase::PointInBufferResult(const Point& pt) { assert( m_result ); int dx = GetWidth(); int dy = GetHeight(); return ( pt.x >= 0 && pt.y >= 0 && pt.x < dx && pt.y < dy ); } void CStenoseNBBase::PutPixEllipse(float xc,float yc,float x,float y) { WritePixel( (int) (xc + x), (int) (yc + y)); WritePixel( (int) (xc - x), (int) (yc - y)); WritePixel( (int) (xc + x), (int) (yc - y)); WritePixel( (int) (xc - x), (int) (yc + y)); } void CStenoseNBBase::WritePixel(int x, int y) { } // Algorithme MidPoint de dessin d'une Ellipse void CStenoseNBBase::myDrawEllipse() { float x = 0, y = 0; double p1, p2, t1, t2; int k; float rx, ry, xc, yc; rx = (float) (m_rcStenose.right - m_rcStenose.left) / 2; ry = (float) (m_rcStenose.bottom - m_rcStenose.top) / 2; xc = m_rcStenose.left + rx; yc = m_rcStenose.top + ry; y = ry; p1 = ry * ry - rx * rx * ry + 0.25 * rx * rx; PutPixEllipse(xc, yc, x, y); for (k = 0; (2 * ry * ry * x) <= (2 * rx * rx * y); k++) { t1 = 2 * ry * ry * x + 2 * ry * ry; t2 = 2 * rx * rx * y - 2 * rx * rx; if (p1 < 0) { p1 = p1 + t1 + ry * ry; } else { p1 = p1 + t1 - t2 + ry * ry; y--; } x++; PutPixEllipse(xc, yc, x, y); } p2 = ry * ry * ( x + 0.5 ) * ( x + 0.5 ) + rx * rx * (y - 1) * (y - 1) - rx * rx * ry * ry; PutPixEllipse(xc, yc, x, y); for (k = 0 ; y >= 0 ; k++) { t1 = 2 * ry * ry * x + 2 * ry * ry; t2 = 2 * rx * rx * y - 2 * rx * rx; if (p2 > 0) { p2 = p2 - t2 + rx * rx; } else { p2 = p2 + t1 - t2 + rx * rx; x++; } y--; PutPixEllipse(xc, yc, x, y); } }