• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

assembler i c++ Filtr Do Zdjec

Aruba Cloud - Virtual Private Server VPS
0 głosów
173 wizyt
pytanie zadane 21 stycznia w Assembler przez Machu03 Nowicjusz (140 p.)

Witam, 
Ostatnio pisząc kod w assemblerze, który implementował filtr do zdjęć napotkałem problem. Próbując zloopować kod, tak, żeby bez wychodzenia z konsoli móc ponownie wypróbować filtr, wyrzuca mi wyjątek na wczytywaniu pikseli. Czy ma ktoś pomysł, jaka może być tego przyczyna?
Poniżej wklejam kawałki kodu. Z góry dziękuję za pomoc.
 

#include <iostream>
#include <fstream>
#include <thread>
#include <omp.h>
#include <chrono>
#include <mutex>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;

extern "C" void alg(unsigned char* ptr, unsigned char* end_row, int width, int height, int start_row);

void algC(unsigned char* imdata, int width, int height, int channels, int stride) {
    unsigned char* output = new unsigned char[(height + 2) * stride];

    int kernel[3][3] = {
        { -1, 1, 1 },
        { -1, -2, 1 },
        { -1, 1, 1 }
    };

    for (int y = 1; y < height - 1; y++) {
        for (int x = 1; x < width - 1; x++) {
            for (int c = 0; c < channels; c++) {
                int gradient = 0;

                for (int ky = -1; ky <= 1; ky++) {
                    for (int kx = -1; kx <= 1; kx++) {
                        int pixelValue = imdata[(y + ky) * stride + (x + kx) * channels + c];
                        gradient += kernel[ky + 1][kx + 1] * pixelValue;
                    }
                }

                int result = gradient;
                if (result < 0) result = 0;
                else if (result > 255) result = 255;

                output[y * stride + x * channels + c] = static_cast<unsigned char>(result);
            }
        }
    }

    for (int i = 0; i < height * stride; ++i) {
        imdata[i] = output[i];
    }

    delete[] output;
}

// Function to process image sections using the C-based algorithm
void process_image_section_c(unsigned char* imdata, int start_row, int end_row, int width, int channels, int stride) {
    unsigned char* section_imdata = imdata + start_row * stride;
    algC(section_imdata, width, end_row - start_row, channels, stride);
}

// Function to process image sections using the Assembly-based algorithm
void process_image_section_asm(unsigned char* imdata, int start_row, int end_row, int width, int channels, int stride) {
    unsigned char* section_imdata = imdata + start_row * stride;
    int height = end_row - start_row;
    unsigned char* output = new unsigned char[height * stride];
    alg(section_imdata, output, width, height, stride);
    memcpy(section_imdata, output, height * stride);
    delete[] output;
}

int main(int argc, char* argv[]) {
    const char* fileName = "C:/Users/machm/OneDrive - Politechnika Śląska/Pictures/lena.jpg";
    Mat image = imread(fileName, IMREAD_COLOR);

    if (image.empty()) {
        std::cerr << "Error: Could not load image." << std::endl;
        return -1;
    }

    cv::namedWindow("Before Processing", WINDOW_NORMAL);
    cv::imshow("Before Processing", image);
    waitKey(0);

    int width = image.cols;
    int height = image.rows;
    int channels = image.channels();
    int stride = image.step;

    while (true) {
        Mat imcopy = image.clone();
        unsigned char* imcopydata = imcopy.data;

        std::cout << "Choose algorithm to process the image:\n";
        std::cout << "1. C++ Algorithm\n";
        std::cout << "2. Assembly Algorithm\n";
        std::cout << "Enter your choice (1 or 2): ";
        int choice;
        std::cin >> choice;

        if (choice != 1 && choice != 2) {
            std::cerr << "Invalid choice! Please select 1 or 2." << std::endl;
            continue;
        }

        int iterations = 1;


        double totalTime = 0.0;

        for (int i = 0; i < iterations; i++) {
            Mat iterCopy = image.clone();
            unsigned char* iterCopyData = iterCopy.data;

            int num_threads = 1;  // Number of threads
            std::vector<std::thread> threads;
            int rows_per_thread = height / num_threads;
            int remaining_rows = height % num_threads;

            auto start = std::chrono::high_resolution_clock::now();

            for (int t = 0; t < num_threads; t++) {
                int start_row = t * rows_per_thread + std::min(t, remaining_rows);
                int end_row = start_row + rows_per_thread + (t < remaining_rows ? 1 : 0);

                if (choice == 1) {
                    threads.emplace_back(process_image_section_c, iterCopyData, start_row, end_row, width, channels, stride);
                } else {
                    threads.emplace_back(process_image_section_asm, iterCopyData, start_row, end_row, width, channels, stride);
                }
            }

            for (auto& thread : threads) {
                thread.join();
            }

            auto end = std::chrono::high_resolution_clock::now();
            auto duration = end - start;
            totalTime += std::chrono::duration<double>(duration).count();

            // Display processed image for the first iteration only
            if (i == 0) {
                cv::namedWindow("Processed", WINDOW_NORMAL);
                cv::imshow("Processed", iterCopy);
                waitKey(0);
            }
        }

        double averageTime = totalTime / iterations;
        std::cout << "Total Time for " << iterations << " iterations: " << totalTime << "s\n";
        std::cout << "Average Time per iteration: " << averageTime << "s\n";

        char again;
        std::cout << "Do you want to run another test? (y/n): ";
        std::cin >> again;
        if (again == 'n' || again == 'N') {
            break;
        }
    }

    return 0;
}
.DATA
    zero_float dd 0.0, 0.0, 0.0, 0.0       ; 4x float 0.0
    max_float dd 255.0, 255.0, 255.0, 255.0 ; 4x float 255.0
    minus_one dd -1.0, -1.0, -1.0, -1.0
    minus_two dd -2.0, -2.0, -2.0, -2.0
;===================PLAN=====================
;przetwarzac jeden piksel na raz
;1 kanal = 4 bajty (?)
;maske zrobic tak jak clampy
;dodawac do jakiegos innego rejestru
;wsadzac do outputu
;mozna na slowach
;lepiej nie na floatach
.CODE
alg PROC
    push rbp
    mov  rbp, rsp
    push rbx
    push rsi
    push rdi
    push r12
    push r13
    push r14
    push r15
    ; data
    mov r15, rcx                     ; r15 = imdata (wskaźnik na dane obrazu)
    mov r11, r8                      ; r11 = width
    mov r12, r9                      ; r12 = height
    mov r13, [rsp+104]               ; r13 = stride (bajty na wiersz)
    mov r14, rdx                     ; r14 = output
	sub r12, 1                       ; wysokosc -1
    sub r11, 1                      
    mov rbx, 1                       ; zaczynamy od pierwszego wiersza

    ; dane do mnozenia/clampowania
    movaps xmm0, [zero_float]
    movaps xmm1, [max_float]
    movaps xmm2, [minus_one]
    movaps xmm3, [minus_two]

row_loop:
    cmp rbx, r12
    je end_loop             ; jesli wiersz==wysokosc -> koniec

    mov rdi, 1              ; zaczynamy od pierwszej kolumny
col_loop:
    
    cmp rdi, r11
    je next_row             ; jesli kolumna==szerokosc -> nastepny wiersz 
    mov rax, rbx            ; rax = y
    imul rax, r13           ; rax = y * stride
    mov rsi, rdi            ; rsi = x
    imul rsi, 3             ; rsi = x * channels
    add rax, rsi            ; rax = y * stride + x * channels
    mov rcx, rax            ; zapisz wskaznik na piksel w rcx
    lea rax, [r15 + rax]    ; wskaznik na obecny piksel w rax

    xorps xmm13, xmm13        ; rejestr do przetrzymywania sumy kanalow


	movd		xmm4, dword ptr[rax]		; centralny piksel w xmm4
    pxor        xmm5, xmm5                  ; zerowanie xmm5
    punpcklbw   xmm4, xmm5                  ; bajty do slow
    punpcklwd   xmm4, xmm5                  ; bajty do dslow
    cvtdq2ps    xmm4, xmm4                  ; konwersja do float
    mulps       xmm4, [minus_two]           ; mnozenie przez -2 kazdego kanalu
    addps       xmm13, xmm4                 ; dodanie do xmm13 (tutaj bedzie suma wszystkich 3 kanalow)

    sub rax, r13                            ; wiersz do gory
    sub rax, 3                              ; piksel w lewo

    movd		xmm4, dword ptr[rax]        ; lewy gorny piksel w xmm4
    pxor        xmm5, xmm5                   
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    mulps       xmm4, [minus_one]           ; mnozenie przez -1 kazdego kanalu
    addps       xmm13, xmm4                 ; dodanie do xmm13
    add rax, 3                              ; piksel w prawo
    movd		xmm4, dword ptr[rax]        ; gorny piksel w xmm4
    pxor        xmm5, xmm5
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    ;mulps       xmm4, [minus_one]           ; mnozenie przez -1 kazdego kanalu
    addps       xmm13, xmm4                 ; dodanie do xmm13

    add rax, 3                              ; piksel w prawo

    movd		xmm4, dword ptr[rax]        ; prawy gorny piksel w xmm4
    pxor        xmm5, xmm5
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    addps       xmm13, xmm4                 ; dodanie do xmm13 (mnozenie przez 1)

    add rax, r13                            ; wiersz w dol

    movd		xmm4, dword ptr[rax]        ; prawy piksel w xmm4
    pxor        xmm5, xmm5
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    addps       xmm13, xmm4                 ; dodanie do xmm13 (mnozenie przez 1)

    sub rax, 6                              ; dwa piksele w lewo

    movd		xmm4, dword ptr[rax]        ; lewy piksel w xmm4
    pxor        xmm5, xmm5
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    mulps       xmm4, [minus_one]           ; mnozenie przez -1 kazdego kanalu
    addps       xmm13, xmm4                 ; dodanie do xmm13

    add rax, r13                            ; wiersz w dol

    movd		xmm4, dword ptr[rax]        ; lewy dolny piksel w xmm4
    pxor        xmm5, xmm5
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    mulps       xmm4, [minus_one] 
    addps       xmm13, xmm4                 ; dodanie do xmm13 (mnozenie przez 1)

    add rax, 3                              ; piksel w prawo

    movd		xmm4, dword ptr[rax]        ; dolny piksel w xmm4
    pxor        xmm5, xmm5
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    addps       xmm13, xmm4                 ; dodanie do xmm13 (mnozenie przez 1)

    add rax, 3                              ; piksel w prawo

    movd		xmm4, dword ptr[rax]        ; prawy dolny piksel w xmm4
    pxor        xmm5, xmm5
    punpcklbw   xmm4, xmm5
    punpcklwd   xmm4, xmm5
    cvtdq2ps    xmm4, xmm4
    addps       xmm13, xmm4                 ; dodanie do xmm13 (mnozenie przez 1)

    cvttps2dq xmm7, xmm13                   ; float to int
    packusdw xmm7, xmm7                     ; zapakowanie do slow
    packuswb xmm7, xmm7                     ; zapakowanie do bajtow
    movd edx, xmm7                          ; zapisanie do edx
    lea rax, [r14 + rcx]                    ; wskaznik na piksel w buforze wyjsciowym

    mov dword ptr [rax], edx                ; zapisanie nowego piksela w buforze wyjsciowym
    inc rdi                                 ; inkrementacja kolumny
    jmp col_loop
next_row:
    inc rbx                                 ; inkrementacja wiersza
    jmp row_loop
end_loop:
    pop r15
    pop r14
    pop r13
    pop r12
    pop rdi
    pop rsi
    pop rbx
    pop rbp
    ret

alg ENDP
END
;-------------------------------------------------------------------------
LIBRARY JADll
EXPORTS alg
;-------------------------------------------------------------------------

 

komentarz 22 stycznia przez overcq Pasjonat (22,440 p.)
Co robi linia 29 w kodzie asemblera? I jak jest zadeklarowany parametr “stride” w prototypie “alg”?
komentarz 22 stycznia przez Machu03 Nowicjusz (140 p.)
Czytając o algorytmie i pytając prowadzącego, jak należałoby go zrobić, prowadzący zalecił mi pomyśleć nad takim parametrem. U mnie w kodzie stride pomaga określić, gdzie zaczyna się każdy wiersz danych pikseli. Przynajmniej taki miałem zamiar, żeby tak robił.
komentarz 23 stycznia przez overcq Pasjonat (22,440 p.)

Parametr w porządku, ale czy w tej linii na pewno go czyta?

W każdym razie u mnie na Linuksie działa taki kod w składni AT&T (tzn. kompiluje się i wykonuje poprawnie):

.data
zero_float:     .float 0.0, 0.0, 0.0, 0.0
max_float:      .float 255.0, 255.0, 255.0, 255.0
minus_one:      .float -1.0, -1.0, -1.0, -1.0
minus_two:      .float -2.0, -2.0, -2.0, -2.0
.text
.global alg
alg:
    push        %rbp
    mov         %rsp,%rbp
    push        %rbx
    push        %r12
    push        %r13
    push        %r14
    push        %r15
    mov         %rdi,%r15
    mov         %rdx,%r11
    mov         %rcx,%r12
    mov         %r8,%r13
    mov         %rsi,%r14
    sub         $1,%r12
    sub         $1,%r11                     
    mov         $1,%rbx
    movaps      zero_float,%xmm0
    movaps      max_float,%xmm1
    movaps      minus_one,%xmm2
    movaps      minus_two,%xmm3
row_loop:
    cmp         %r12,%rbx
    je          end_loop
    mov         $1,%rdi
col_loop:
    cmp         %r11,%rdi
    je          next_row
    mov         %rbx,%rax
    imul        %r13,%rax
    mov         %rdi,%rsi
    imul        $3,%rsi
    add         %rsi,%rax
    mov         %rax,%rcx
    lea         (%r15,%rax),%rax
    xorps       %xmm13,%xmm13
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    mulps       minus_two,%xmm4
    addps       %xmm4,%xmm13
    sub         %r13,%rax
    sub         $3,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5                   
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    mulps       minus_one,%xmm4
    addps       %xmm4,%xmm13
    add         $3,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    ;mulps       minus_one,%xmm4
    addps       %xmm4,%xmm13
    add         $3,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    addps       %xmm4,%xmm13
    add         %r13,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    addps       %xmm4,%xmm13
    sub         $6,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    mulps       minus_one,%xmm4
    addps       %xmm4,%xmm13
    add         %r13,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    mulps       minus_one,%xmm4
    addps       %xmm4,%xmm13
    add         $3,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    addps       %xmm4,%xmm13
    add         $3,%rax
    movd        (%rax),%xmm4
    pxor        %xmm5,%xmm5
    punpcklbw   %xmm5,%xmm4
    punpcklwd   %xmm5,%xmm4
    cvtdq2ps    %xmm4,%xmm4
    addps       %xmm4,%xmm13
    cvttps2dq   %xmm13,%xmm7
    packusdw    %xmm7,%xmm7
    packuswb    %xmm7,%xmm7
    movd        %xmm7,%edx
    lea         (%r14,%rcx),%rax
    mov         %edx,(%rax)
    inc         %rdi
    jmp         col_loop
next_row:
    inc         %rbx
    jmp         row_loop
end_loop:
    pop         %r15
    pop         %r14
    pop         %r13
    pop         %r12
    pop         %rbx
    pop         %rbp
    ret

U Ciebie na Windowsie potrzebujesz zmienić rejestry zgodnie z konwencją wywołania.

1 odpowiedź

0 głosów
odpowiedź 21 stycznia przez reaktywny Nałogowiec (46,230 p.)
edycja 21 stycznia przez reaktywny
Najczęstszy przypadek w tego typu problemach to wyjście poza zakres tablicy.
Nie wiem czy tu też masz z tym problem, ale jest to powszechny błąd.

Widzę, że tu kod jest poważniejszy, wykorzystujesz wątki - może tu coś się wkradło?

Ten program jest rozbudowany, w jakim celu używasz OpenCV?, skoro filtry można z łatwością aplikować samodzielnie bez użycia OpenCV.
komentarz 21 stycznia przez Machu03 Nowicjusz (140 p.)
Exception thrown at 0x00007FFF5CFA1090 (JADll.dll) in jAAppImage.exe: 0xC0000005: Access violation reading location 0x000002BBE87617C3.

takie exception dostaje przy probie ponownego wczytania tego samego, obrazu.

W jaki sposob moge zadbac o to, zeby ten zakres tablicy byl poprawny?
komentarz 21 stycznia przez reaktywny Nałogowiec (46,230 p.)
Linia 23-24, jesteś pewien, ze y i x zaczynają od 1 ? (tylko pytam).
komentarz 21 stycznia przez reaktywny Nałogowiec (46,230 p.)
Sprawdziłbym też czy dobrze dzielisz obrazek na poszczególne wątki. Lena Leną, ale obrazki mają różny rozmiar.
komentarz 21 stycznia przez reaktywny Nałogowiec (46,230 p.)
Ta cała pętla z wątkami jest źle IMO, ale to koledzy na forum specjalizujący się w C++ wyklarują na czym polegają błędy.
komentarz 21 stycznia przez Machu03 Nowicjusz (140 p.)

@reaktywny, spróbowałem odpalić z 0, zamiast 1, nie widziałem różnicy w obrazie, może faktycznie zaczynają się od 0

Testując sam algorytm na dowolnych obrazkach bez dodatkowych iteracji, wygląda on dobrze, niezależnie od wielkości obrazu.

Debugując kod, zauważyłem, że w miejscu wyrzucania wyjątku, przy drugiej iteracji kodu, podawane mu są kompletnie inne liczby, rejestry maja inne wartości, nie do końca rozumiem dlaczego skoro przecież podstawowy obrazek powinien być niezmieniony, a sam algorytm pracuje na kopi obrazu. Jaki moze byc tego powod?

Ok, w przypadku pętli poczekam może na odpowiedź jakiś ludzi zajmujących się c++
 

komentarz 21 stycznia przez reaktywny Nałogowiec (46,230 p.)
Tablicę wątków zakłada się przed pętlą w której przydzielasz wątkom zadania. Ale tu koledzy pomogą. Nie jestem pewien, ale chyba też źle dzielisz obraz na watki.
komentarz 21 stycznia przez Machu03 Nowicjusz (140 p.)
Ok, próbowałem cos pozmieniać, ale w momencie zmiany wątków, kod assemblera przestał działać. Nie wiem dlaczego, może poczekam na jakąś pomoc z c++

Podobne pytania

0 głosów
1 odpowiedź 779 wizyt
pytanie zadane 12 kwietnia 2018 w Assembler przez Isild Użytkownik (620 p.)
0 głosów
1 odpowiedź 469 wizyt
pytanie zadane 12 stycznia 2019 w Assembler przez Puacz Nowicjusz (150 p.)
0 głosów
1 odpowiedź 502 wizyt
pytanie zadane 9 czerwca 2021 w Assembler przez anipic Nowicjusz (150 p.)

93,324 zapytań

142,323 odpowiedzi

322,390 komentarzy

62,653 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Wprowadzenie do ITsec, tom 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...