-
Notifications
You must be signed in to change notification settings - Fork 1
Feat 26/adicao pontos #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
wandersonbatista
wants to merge
44
commits into
main
Choose a base branch
from
feat-26/adicao-pontos
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
3208338
feat: pytest adicionado
gabrielfruet d9463fe
feat: inicialização da curva
gabrielfruet 87b3215
feat: vetorizacao na funcao de inicializacao de curva
gabrielfruet fb15580
fix: os pontos da curva devem ser sempre maiores que 0
gabrielfruet b9cb61d
fix: quantidade de pixels deve ser maior que 2
gabrielfruet 2738fb1
feat: testes para inicializa_curva
gabrielfruet 9ba0bf9
chore: testando a branch #6
pauloricms12 9869005
Merge branch 'dev' into feat-5/inicializacao-curva
gabrielfruet 76a8cca
Merge pull request #8 from ProjetoFinalPDI/feat-5/inicializacao-curva
gabrielfruet 59ca8c2
feat: adiciona crisp inicial #6
pauloricms12 273eb3d
feat: formatacao com ruff #6
pauloricms12 f3655d5
feat#3.3/Implementada a lógica sem a função do Sobel e ambiente de te…
MateusSantos14 a0db6c4
feat#3.3/Formatado ruff e documentado
MateusSantos14 82a700b
feat#3.4/ Adicionado operador Sobel
MateusSantos14 64dedc9
feat#3.4/Feito
MateusSantos14 126ec18
Merge pull request #9 from ProjetoFinalPDI/feat4-energiaexcrisp
gabrielfruet 6f2b117
Implementação vetorizada
MateusSantos14 a947fe7
Implementação vetorizada
MateusSantos14 ed3615f
feat#3.4/Implementação vetorizada
MateusSantos14 54ff3f9
feat: força de continuidade
Davi0Cruz d83fc26
fix: snake case
Davi0Cruz a4544ca
feat 7: testes força de continuidade
Davi0Cruz 4b551a4
Adicionada a funcionalidade de forca adaptativa (issue #12)
dae11e1
Merge pull request #13 from ProjetoFinalPDI/feat4-energiaexcrisp
gabrielfruet 5a19214
Code fixes
rafaelcapeloo 1cbf982
add .vscode to git ignore
Davi0Cruz 284db7b
feat 7: testes
Davi0Cruz f9589d7
Merge branch 'dev' into feat-7/forca-continuidade
Davi0Cruz a9ae12e
Merge pull request #18 from ProjetoFinalPDI/feat-12/forca-adaptativa
gabrielfruet 7c5407a
Aplicação da vetorização.
2ad855f
Merge branch 'dev' into feat-7/forca-continuidade
gabrielfruet f02b560
Merge pull request #19 from ProjetoFinalPDI/feat-7/forca-continuidade
gabrielfruet 5ee5181
Merge branch 'dev' into feat-12/forca-adaptativa
gabrielfruet af13d62
Merge pull request #20 from ProjetoFinalPDI/feat-12/forca-adaptativa
gabrielfruet 6d3d871
Função para converter HU para escala de cinza
Davidls10 4d47455
Merge branch 'feat-6/crisp-inicial' into dev
gabrielfruet c80d006
Merge pull request #23 from ProjetoFinalPDI/dev
gabrielfruet 92889d0
Merge pull request #25 from ProjetoFinalPDI/feat-24/hu_para_cinza
Davidls10 8235a74
feat: adiciona remocao de pontos #27
pauloricms12 739a505
Merge pull request #30 from ProjetoFinalPDI/feat-27/remover-pontos-da…
gabrielfruet bfd9f7b
feat: energia interna adaptativa
gabrielfruet 7896018
fix: correções no fluxo de trabalho da main.py
gabrielfruet f515ebe
Merge pull request #33 from ProjetoFinalPDI/feat-31/energia-interna-a…
gabrielfruet d0b6cda
Add files via upload
wandersonbatista File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| .venv/ | ||
| __pycache__/ | ||
| .ruff_cache/ | ||
| .vscode/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .PHONY: test | ||
| test: | ||
| pytest ./tests --rootdir=. | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import numpy as np | ||
|
|
||
| def converter_hu_para_cinza(imagem_hu, hu_min=-1000, hu_max=2000) -> np.ndarray: | ||
| """ | ||
| Converte Hounsfield Units (HU) para escala de cinza. | ||
|
|
||
| args: | ||
| input_path: np.ndarray - Imagem em Hounsfield Units (HU) | ||
| return: | ||
| np.ndarray - Imagem em escala de cinza | ||
| """ | ||
|
|
||
| largura_hu = hu_max - hu_min | ||
|
|
||
| imagem_hu = imagem_hu.astype(np.float32) | ||
| imagem_escala_cinza = np.clip((255 * (imagem_hu - hu_min)) / largura_hu, 0, 255) | ||
|
|
||
| return imagem_escala_cinza.astype(np.uint8) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [pytest] | ||
| pythonpath = . |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A adição de pontos não deveria lidar com a energia. Boa idaide usar a função do matplotlib! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import numpy as np | ||
| from matplotlib.path import Path | ||
| from energia import energia_interna_adaptativa | ||
| from carregar import carregar_imagem | ||
| from classificacao import calcula_ocorrencias_classes, probabilidade_classes | ||
|
|
||
|
|
||
| def adicionar_pontos( | ||
| curva: np.ndarray, imagem: np.ndarray, d: float, w_adapt=0.1, w_cont=0.6 | ||
| ) -> np.ndarray: | ||
| """ | ||
| Adiciona pontos à curva minimizando a energia e garantindo que pertencem ao pulmão. | ||
|
|
||
| Args: | ||
| curva (np.ndarray): Pontos da curva inicial. | ||
| imagem (np.ndarray): Imagem DICOM carregada em Unidades Hounsfield. | ||
| d (float): Distância mínima entre pontos. | ||
| w_adapt (float): Peso da energia adaptativa. | ||
| w_cont (float): Peso da energia de continuidade. | ||
|
|
||
| Returns: | ||
| np.ndarray: Curva refinada com pontos adicionados. | ||
| """ | ||
| nova_curva = [curva[0]] | ||
| poligono = Path(curva) # Criar polígono da curva para verificação de inclusão | ||
|
|
||
| # Calcular probabilidades das classes pulmonares | ||
| ocorrencias = calcula_ocorrencias_classes(imagem) | ||
| probabilidades = probabilidade_classes(ocorrencias) | ||
|
|
||
| for i in range(len(curva) - 1): | ||
| p1, p2 = curva[i], curva[i + 1] | ||
| dist = np.linalg.norm(p2 - p1) | ||
|
|
||
| if dist > d: | ||
| num_pontos = int(dist // d) | ||
| melhor_pontos = [] | ||
|
|
||
| for j in range(1, num_pontos + 1): | ||
| candidato = p1 + (p2 - p1) * (j / (num_pontos + 1)) | ||
|
|
||
| # Verificar se o ponto está dentro da curva | ||
| if poligono.contains_point(candidato): | ||
| x, y = int(candidato[0]), int(candidato[1]) | ||
|
|
||
| # Verificar se o ponto pertence ao pulmão (hiperaerado e normalmente aerado) | ||
| if -1000 <= imagem[y, x] <= -500: | ||
| energia = energia_interna_adaptativa(curva, i, w_adapt, w_cont) | ||
| prob_pulmao = ( | ||
| probabilidades[0, y, x] + probabilidades[1, y, x] | ||
| ) # Soma das classes pulmonares | ||
|
|
||
| # Só adiciona o ponto se a probabilidade de pulmão for alta | ||
| if prob_pulmao > 0.5: | ||
| melhor_pontos.append((energia, candidato)) | ||
|
|
||
| # Ordenar os pontos pela menor energia | ||
| melhor_pontos.sort(key=lambda x: x[0]) | ||
| nova_curva.extend([p[1] for p in melhor_pontos]) | ||
|
|
||
| nova_curva.append(p2) | ||
|
|
||
| return np.array(nova_curva) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| import numpy as np | ||
|
|
||
|
|
||
| def crisp_inicial( | ||
| imagem: np.ndarray, | ||
| lim_infY: int, | ||
| lim_supY: int, | ||
| lim_infX: int, | ||
| lim_supX: int | ||
| ) -> np.ndarray: | ||
| """ | ||
| Recebe uma imagem em formato de array NumPy e recorta a região definida pelos | ||
| limites superiores e inferiores (tanto em X quanto em Y). A função identifica | ||
| a posição onde há maior concentração de pixels dentro da faixa de intensidade | ||
| [-1000, -500] e retorna as coordenadas correspondentes. | ||
|
|
||
| Args: | ||
| imagem (np.ndarray): Matriz da imagem de entrada. | ||
| lim_infY (int): Limite inferior do recorte na direção vertical (eixo Y). | ||
| lim_supY (int): Limite superior do recorte na direção vertical (eixo Y). | ||
| lim_infX (int): Limite inferior do recorte na direção horizontal (eixo X). | ||
| lim_supX (int): Limite superior do recorte na direção horizontal (eixo X). | ||
|
|
||
| Returns: | ||
| np.ndarray: Coordenadas (x, y) do ponto com maior concentração de pixels | ||
| dentro da faixa de interesse. | ||
|
|
||
| Raises: | ||
| ValueError: Se os limites fornecidos forem inválidos, ou seja, se | ||
| lim_supY < lim_infY ou lim_supX < lim_infX. | ||
|
|
||
| Example: | ||
| >>> from carregar import carregar_imagem | ||
| >>> imagem = carregar_imagem("../data/pulmao2/60.dcm") | ||
| >>> centro_Esq = crisp_inicial( | ||
| ... imagem=imagem, lim_infY=180, lim_supY=360, lim_infX=0, lim_supX=255 | ||
| ... ) | ||
| array([172, 266]) | ||
| >>> centro_dir = crisp_inicial( | ||
| ... imagem=imagem, | ||
| ... lim_infY=180, | ||
| ... lim_supY=360, | ||
| ... lim_infX=256, | ||
| ... lim_supX=512, | ||
| ... ) | ||
| array([335, 289]) | ||
| """ | ||
| if lim_supY < lim_infY or lim_supX < lim_infX: | ||
| raise ValueError("Os limites fornecidos para X e Y são inválidos.") | ||
|
|
||
| # Recorta a Imagem nos Limites Fornecidos | ||
| imagem = imagem[lim_infY : lim_supY + 1, lim_infX : lim_supX + 1] | ||
| P = np.where((imagem > -1000) & (imagem < -500), 1, 0) | ||
|
|
||
| # Soma os pixels ao longo dos eixos | ||
| X = np.sum(P, axis=1) # Soma na direção das colunas | ||
| Y = np.sum(P, axis=0) # Soma na direção das linhas | ||
|
|
||
| return np.array([np.argmax(Y) + lim_infX, np.argmax(X) + lim_infY]) | ||
|
|
||
|
|
||
| def inicializa_curva( | ||
| ponto: np.ndarray, | ||
| raio: int = 30, | ||
| quantidade_pixels: int = 30, | ||
| ) -> np.ndarray: | ||
| """ | ||
| Recebe o ponto inicial do eixo x e y, calcula a curva e retorna a duas | ||
| listas de pontos que representam a curva. | ||
|
|
||
| Args: | ||
| ponto: (x,y) que representa o centro da curva | ||
| raio: raio da curva | ||
| quantidade_pixels: quantidade de pontos que a curva terá | ||
| Return: | ||
| curva: lista de pontos que representam a curva | ||
| Raises: | ||
| AssertionError: caso curva seja menor que 0 em algum ponto | ||
| AssertionError: caso a quantidade_pixels seja menor que 2 | ||
| """ | ||
| assert quantidade_pixels >= 2, "Quantidade de pixels deve ser sempre maior que 2" | ||
|
|
||
| angulos = np.linspace(0, 2 * np.pi, quantidade_pixels+1) | ||
| curva = ponto + np.c_[np.cos(angulos), np.sin(angulos)] * raio | ||
|
|
||
| assert np.all(curva > 0), "Os pontos da curva devem sempre ser maior que 0" | ||
|
|
||
| return curva[:-1].astype(np.int16) | ||
|
|
||
| def calcular_angulo(p1:np.ndarray, | ||
| p2:np.ndarray, | ||
| p3:np.ndarray | ||
| ) -> float: | ||
| """ | ||
| Calcula o ângulo formado pelos três pontos (em graus). | ||
| Args: | ||
| pontos p1(i-1), p2(i) e p3(i+1) | ||
| Return: | ||
| angulo em graus entre os 3 pontos | ||
|
|
||
| """ | ||
| v1 = p1 - p2 | ||
| v2 = p3 - p2 | ||
|
|
||
| cos_theta = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)) | ||
| cos_theta = np.clip(cos_theta, -1.0, 1.0) #Manter o cos entre -1 e 1 | ||
|
|
||
| return np.degrees(np.arccos(cos_theta)) | ||
|
|
||
|
|
||
| def remover_pontos( | ||
| curva: np.ndarray, | ||
| alpha: float = 20 | ||
| ) -> np.ndarray: | ||
| """ | ||
| Recebe pontos da curva e um ângulo mínimo para remover pontos da curva. | ||
|
|
||
| Args: | ||
| curva (np.darray): Pontos da curva. | ||
| alpha (float): Ângulo mínimo entre dois pontos para remover da curva. | ||
| Returns: | ||
| np.darray: Curva com os pontos removidos. | ||
| """ | ||
|
|
||
| assert curva.shape[-1] == 2, "A curva deve ser um array de shape [n,2]" | ||
|
|
||
| curva = curva[np.insert(np.any(np.diff(curva, axis=0), axis=1), 0, True)] | ||
|
|
||
| curva_filtrada = [curva[0]] | ||
|
|
||
| for i in range(1, len(curva) - 1): | ||
| angulo = calcular_angulo(curva[i-1], curva[i], curva[i+1]) | ||
| if angulo > alpha: | ||
| curva_filtrada.append(curva[i]) | ||
|
|
||
| curva_filtrada.append(curva[-1]) | ||
| return np.array(curva_filtrada, dtype=np.int16) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import numpy as np | ||
| import cv2 | ||
|
|
||
| from segmentacao.forca import forca_adaptativa, forca_continuidade | ||
|
|
||
|
|
||
| def energia_externa( | ||
| imagem: np.ndarray, | ||
| probabilidade: np.ndarray, | ||
| probablidade3: float = 0.2, | ||
| probablidade4: float = 0.15, | ||
| ) -> np.ndarray: | ||
| """ | ||
| Recebe a imagem e as probabilidades de ocorrência de classe da imagem e retorna | ||
| a matriz com energia externa crisp de cada ponto imagem. Pode receber os limiares | ||
| das probabilidade P(3) e P(4) para experimentação posterior. | ||
|
|
||
| Args: | ||
| imagem (np.ndarray): Imagem para calcular a energia. | ||
| probabilidade (np.ndarray): Probabilidade de ocorrência de classe de todos os | ||
| pontos. | ||
| probablidade3 (float): Limiar da probabilidade 3 para definir o valor da energia | ||
| crispy como 0. | ||
| probablidade4 (float): Limiar da probabilidade 4 para definir o valor da energia | ||
| crispy como 0. | ||
| Return: | ||
| energia (np.ndarray): Matriz das energias crispy de todos os pontos da imagem. | ||
| """ | ||
| # Cálculo do Sobel | ||
| sobel_x = cv2.Sobel(imagem, cv2.CV_64F, 1, 0, ksize=3) | ||
| sobel_y = cv2.Sobel(imagem, cv2.CV_64F, 0, 1, ksize=3) | ||
| energia = np.sqrt(sobel_x**2 + sobel_y**2) | ||
|
|
||
| # Mascara de probabilidade | ||
| mask = (probabilidade[2] >= probablidade3) | (probabilidade[3] > probablidade4) | ||
| energia[~mask] = 0 | ||
|
|
||
| return energia | ||
|
|
||
|
|
||
| def energia_interna_adaptativa( | ||
| curva: np.ndarray, indice: int, w_adapt: float = 0.1, w_cont: float = 0.6 | ||
| ): | ||
| adaptativa = w_adapt * forca_adaptativa(curva, indice) | ||
| continuidade = w_cont * forca_continuidade(curva, indice) | ||
| return adaptativa + continuidade |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import numpy as np | ||
|
|
||
|
|
||
| def forca_continuidade(pontos: np.ndarray, indice: int) -> np.float64: | ||
| """ | ||
| Calcula a força de continuidade da curva em um ponto. | ||
|
|
||
| Args: | ||
| pontos: np.array - Pontos da curva. | ||
| indice: int - Índice do ponto. | ||
| Return: | ||
| float - Força de continuidade. | ||
| """ | ||
|
|
||
| # Calcular a distância média entre os pontos | ||
| dm = np.linalg.norm(pontos - np.roll(pontos, 1, axis=0), axis=1).mean() | ||
|
|
||
| # Calcular a derivada discreta do ponto | ||
| dc = np.linalg.norm(pontos[indice] - pontos[indice - 1]) | ||
|
|
||
| return np.abs(dm - dc) | ||
|
|
||
|
|
||
| def forca_adaptativa(pontos: np.ndarray, indice: int) -> np.float64: | ||
| """ | ||
| Recebe os pontos em ordem anti-horária da curva e calcula a força adaptativa | ||
| no ponto de índice 'indice'. | ||
|
|
||
| Args: | ||
| pontos (np.ndarray): Pontos da curva. | ||
| indice (int): Índice do ponto. | ||
| Returns: | ||
| float: Força adaptativa no ponto de índice 'indice'. | ||
| """ | ||
| pm = (pontos[(indice - 1) % len(pontos)] + pontos[(indice + 1) % len(pontos)]) / 2 | ||
| v1 = pontos[(indice + 1) % len(pontos)] - pontos[indice] | ||
| v2 = pontos[(indice - 1) % len(pontos)] - pontos[indice] | ||
| vet = np.sign(np.linalg.det([v1, v2])) | ||
| if vet == 0: | ||
| return np.float64(0.0) | ||
| return np.linalg.norm(pm + vet * pontos[indice]) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
acho que não precisa de makefile