Filtr splotowy obrazu - Qt

Filtr splotowy obrazu - Qt
K4
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 2 lata
0

Witam

Mam za zadanie napisać algorytm Filtru splotowego.

Wzorowałem się na artykule z pewnego blogu, jest tam również przykładowy kod lecz C# nie umiem za dobrze więc napisałem swój własny algorytm w oparciu o tekst.

http://dikamilo.net/entry/filtry-splotowe-w-c-sharp/

Mój problem polega na tym że dla danych wartości w masce algorytm albo rozmywa albo wyostrza prawie zawsze w ten sam sposób. Tak jak by wartości w masce nie miały większego znaczenia. Przykładowe wypełnienia maski na dole artykułu nie działają niestety u mnie...

Algorytm jeszcze bez warunków brzegowych dlatego te 20 w petlach.
Algorytm trzeba wykonać dla wszystkich pixeli obrazka i jak i składowych RGB.

tab[][] - maska.
r2 - promień maski (dla 3x3 maski = 1, dla 5x5 = 2 itd....)
clamp() - zaokrągla wartości do przedziału 0-255.

Kopiuj
void Splot::maska(QImage *img)
{
    QImage img_copy = *img;

    uchar *p = 0;

    for(int y=20; y<img->height()-20; ++y)
    {
        for(int x=20; x<4*img->width()-20; x+=4)
        {
            double  sumaX = 0;
            double  suma_mnozenia_R = 0,
                    suma_mnozenia_G = 0,
                    suma_mnozenia_B = 0;

            int a = -1,b = -1;

            for (int j = y-r2; j< y+r2; j++)
            {
                p = img_copy.scanLine(j);
                b++;
                for (int i = x-r2*4; i< x+r2*4; i+=4)
                {
                    a++;
                    sumaX += tab[a][b];

                    suma_mnozenia_R += p[2+i] * tab[a][b];
                    suma_mnozenia_G += p[1+i] * tab[a][b];
                    suma_mnozenia_B += p[i] * tab[a][b];
                }
            }

            if (sumaX == 0)
                continue;
            else
            {
                p = img->scanLine(y);
                p[x+2] = clamp(suma_mnozenia_R / sumaX,0,255);
                p[x+1] = clamp(suma_mnozenia_G / sumaX,0,255);
                p[x  ] = clamp(suma_mnozenia_B / sumaX,0,255);
            }
        }
    }
}

Wartości do maski wczytuje z QTableWidget - działa to 100%.
Cała reszta też powinna działać prawidłowo, problem jest raczej z tym algorytmem.

MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
1

Splot to jest coś co przyjmuje dwie funkcje/obrazy a zwraca jedną funkcję/obraz. To ma być coś takiego (dla QImage z 32 bitowym kolorem RGB).

Kopiuj
class Maska {
public:
    virtual ~Maska() {}
    virtual int width() const = 0;
    virtual int height() const = 0;
    virtual int norm() const = 0;
    virtual const int *operator[](int row) const  = 0;
};

template<int w, int h>
class DefMask : public Maska {
     int data[h][w];
     mutable int norma;
public:
    DefMask () : norma(0) {
        std::fill(data[0], data[0]+h*w, 0);
    }

    int width() const {return w;}
    int height() const {return h;}
    int norm() const {
        if (!norma)
            norma = policzNorme();
        return norma;
    }
    const int *operator[](int row) const { return data[row]; }
    int *operator[](int row) { norma= 0; return data[row]; }
    int policzNorme() const {
         int suma = 0;
         for(int i=0; i<h; ++i)
         for(int j=0; j<w; ++j)
             suma += data[i][j]*data[i][j];
         return ceil(sqrt(suma*w*h));
    }
};

QImage splot(const QImage& img, const Maska &maska) {
    Q_ASSERT(QImage::Format_RGB32 == img.format());
    int maskW(maska.width()), maskH(maska.height());
    QImage result(img.width()-maskW, img.height()-maskH, img.format());
    int norm = maska.norm();

    for (int y=0; y<result.height(); ++y) {
         for (int x=0; x<result.width(); ++x) {
             int r(0), g(0), b(0);
             for (int i=0; i<maskH; ++i) {
                 const int *maskRow = maska[i];
                 const QRgb *imgLine = reinterpret_cast<const QRgb*>(img.scanLine(y+i));
                 for(int j=0; j<maskW; ++j) {
                      QRgb color = imgLine[j+x];
                      b += maskRow[j]*qBlue(color);
                      g += maskRow[j]*qGreen(color);
                      r += maskRow[j]*qRed(color);
                 }
             }
             r = qBound(0, r/norm, 0xff);
             g = qBound(0, g/norm, 0xff);
             b = qBound(0, b/norm, 0xff);
             result.setPixel(x,y,qRgb(r,g,b));
         }
    }
   return result;
}

Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 9x, ostatnio: MarekR22
K4
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 2 lata
0

Dość ciężki kod jak dla mnie, ale spróbuje przetestować.

Maska w moim zadaniu ma być po prostu 2 wymiarową tablicą intów o nieparzystym rozmiarze (np. 5x5). Więc moim zdaniem te dwie klasy nie są konieczne.

Jeszcze nie mogę dojść co to jest to norm

Kopiuj
int norm() const { return 10; }
MarekR22
z tymi dwiema klasami to poszedłem na skróty, używając szablonów i polimorfizmu, więc jeśli chcesz 5x5 to DefMask&lt;5,5&gt; maska załatwia sprawę.
K4
Ok dzięki, Dla mnie najlepiej (najłatwiej) było by olać klasy i dać po prostu int** maska, I całość trzymać w 1 klasie :)
MarekR22
norm() tu chodzi o normalizację maski. Poprawiłem z bardziej prawidłową implementacją, która liczy normę w ten sposób, by utrzymać jednakową jasność obrazu. Jeśli ci to przeszkadza, to wstaw tam jedynkę. Twoja decyzja.
K4
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 2 lata
0

Witam

dziś wczoraj wziąłem się za kod, ale nie mogę go poprawnie dodać do mojego programu.

Kilka uwag i pytań zarazem.

  1. Klasa Maska jest klasą abstrakcyjną ? czyż funkcje w niej nie powinny być wirtualne ? Inaczej błąd :initializer specified for non-virtual method 'int Maska::width() const' itp...

Kopiuj
const quint32 *imgLine = static_cast<const quint32 *>(img.scanLine(y+i));

Błąd : błąd:invalid static_cast from type 'uchar*' to type 'const quint32*', Czy po prostu nie lepiej skanować do uchar* ?

3.przy próbie utworzenia obiektu -

Kopiuj
 DefMask<5,5> mask;

tutaj wywala błąd

Kopiuj
int norm() const {
        if (!norma)
            norma = policzNorme();
        return norma;
}

passing 'const DefMask<5, 5>' as 'this' argument of 'int DefMask<w, h>::policzNorme() [with int w = 5, int h = 5]' discards qualifiers
usunięcie const niby pomaga ale pewnie to nie jest rozwiązanie problemu.

Kopiuj
Q_ASSERT(QImage::Format_RGB888 == img.format());

Tutaj mi w ogóle program wywala - wczytuję .bmp 24 bitowe.

Gdy w końcu jakoś udało mi się skompilować dostaje zawsze czarny obraz...

Kod funkcji na chwilę obecną:

Kopiuj
QImage Splot::fun(QImage img)
{
    mask.toData(tab,r);

    //Q_ASSERT(QImage::Format_RGB888 == img.format());

    QImage result(img.width()-r, img.height()-r, img.format());

    int norm = mask.norm();

    for (int y=0; y<result.height(); ++y) {
             for (int x=0; x<result.width(); ++x) {
                 int r(0), g(0), b(0);
                 for (int i=0; i<r; ++i) {
                     const uchar *maskRow = mask[y+i];
                     const uchar *imgLine = img.scanLine(y+i);
                     for(int j=0; j<r; ++j) {
                          uchar color = imgLine[j+x]; // typ ?
                          b += maskRow[j]*qBlue(color);
                          g += maskRow[j]*qGreen(color);
                          r += maskRow[j]*qRed(color);
                     }
                 }
                 r = qBound(0, r/norm, 0xff);
                 g = qBound(0, g/norm, 0xff);
                 b = qBound(0, b/norm, 0xff);
                 result.setPixel(x,y,qRgb(r,g,b));
             }
        }
    return result;
}

Maskę mam zdefiniowaną w .h

MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
0

Pisałem z palca wiec, brakuje tego i owego.
Ad1. tak brakowało słowa virtual
Ad2. powinno być reinterpret_cast
Ad3. na to będę musiał spojrzeć samo potem. Spokojnie możesz tam wpisać stałą, np. 1.
Ad4. moja implementacja dotyczy konkretnego formatu QImage ta asercja sprawdza, czy format jest poprawny. Masz dwa wyjścia: napisać obsługę innych formatów, albo wykonywać konwersję QImage do formatu obsługiwanego przez moją funkcję.

Ogólnie napisałem funkcję poglądową nad detalami możesz się sam pomęczyć.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 1x, ostatnio: MarekR22
K4
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 2 lata
0

Pomijając już sposób implementacji największym problemem jest taki że ani mój (ten z 1 postu) ani Twój algorytm nie działa mi poprawnie...a żeby poprawiać detale chciałbym chociaż aby to coś mi w końcu zadziałało bo męczę się z tym któryś dzień a dalej nie wiem gdzie jest błąd.

MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
1

Edytując post poprawiłem swój kod ze wszystkich literówek (było ich troszkę :) ). Poza tym dla wygody zmieniłem format obrazka na QImage::Format_RGB32, bo okazało się, że w formacie QImage::Format_RGB888 piksele są ciasno upakowane, a nie wyrównywane do podwójnego słowa. Przed użyciem tej funkcji należy zadbać by QImage był we właściwym formacie:

Kopiuj
if (image.format()!=QImage::Format_RGB32)
    image=image.convertToFormat(QImage::Format_RGB32);

Kod testowałem i raczej działa jak należy.


a tu napisałem maskę którą można edytować za pomocą QTableView (fajny przykład jak należy korzystać z modeli danych):
  • nagłówek:
Kopiuj
#ifndef EDITABLEMASK_H
#define EDITABLEMASK_H

#include <QAbstractTableModel>
#include "maska.h"

const int MaxMaskSize = 16;
const int MaxMaskRadius = (MaxMaskSize-1)/2;

class EditableMask : public QAbstractTableModel , public Maska
{
    Q_OBJECT

    Q_PROPERTY(int radius
               READ radius
               WRITE setRadius
               NOTIFY radiusChanged)

    Q_PROPERTY(int norm
               READ norm
               WRITE setNorm
               NOTIFY normChanged
               RESET setDefaultNorm)

public:
    explicit EditableMask(QObject *parent = 0);
    
    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;

    bool setData(const QModelIndex &index, const QVariant &value, int role);
    Qt::ItemFlags flags(const QModelIndex &index) const;

    int width() const;
    int height() const;
    int norm() const;
    const int *operator[](int row) const;

    int radius() const;

    int defaultNorm() const;

signals:
    void radiusChanged(int);
    void normChanged(int);
    
public slots:
    void setRadius(int);

    void setNorm(int norma);
    void setDefaultNorm();
    
private:
    int mOffset;
    int mSize;
    mutable int mNorma;
    int mData[MaxMaskSize][MaxMaskSize];
};

#endif // EDITABLEMASK_H
  • plik z kodem:
Kopiuj
#include "editablemask.h"

EditableMask::EditableMask(QObject *parent) :
    QAbstractTableModel(parent)
{
    std::fill(mData[0], mData[0]+MaxMaskSize*MaxMaskSize, 0);
    mSize = 3;
    mOffset = MaxMaskRadius - 1;
    mData[MaxMaskRadius][MaxMaskRadius] = 1;
    mNorma = 1;
}

int EditableMask::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return mSize;
}

int EditableMask::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return mSize;
}

QVariant EditableMask::data(const QModelIndex &index, int role) const
{
    switch(role) {
    case Qt::DisplayRole:
    case Qt::EditRole:
        return QString::number(mData[index.row()+mOffset][index.column()+mOffset]);
    default:
        return QVariant();
    }
}

bool EditableMask::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (role!=Qt::EditRole)
        return false;
    bool ok = false;
    int x = value.toInt(&ok);
    if (ok) {
        mData[index.row()+mOffset][index.column()+mOffset] = x;
        return true;
    }
    return false;
}

Qt::ItemFlags EditableMask::flags(const QModelIndex &index) const
{
    Q_UNUSED(index)
    return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

int EditableMask::width() const
{
    return mSize;
}

int EditableMask::height() const
{
    return mSize;
}

int EditableMask::norm() const
{
    return mNorma;
}

const int *EditableMask::operator [](int row) const
{
    return mData[mOffset+row]+mOffset;
}

int EditableMask::radius() const
{
    return (mSize-1)/2;
}

void EditableMask::setRadius(int newRadius)
{
    int oldRadius = (mSize-1)/2;
    newRadius = qBound(1, newRadius, MaxMaskRadius);
    if (newRadius>0 && oldRadius!=newRadius) {
        int diff = newRadius-oldRadius;
        if (diff>0) {
            beginInsertRows(   QModelIndex(), 0, diff-1);
            beginInsertColumns(QModelIndex(), 0, diff-1);
            mOffset -= diff;
            mSize += diff;
            endInsertRows();
            endInsertColumns();

            beginInsertRows(   QModelIndex(), mSize, mSize + diff-1);
            beginInsertColumns(QModelIndex(), mSize, mSize + diff-1);
            mSize += diff;
            endInsertRows();
            endInsertColumns();
        } else {
            beginRemoveRows(   QModelIndex(), 0, -diff-1);
            beginRemoveColumns(QModelIndex(), 0, -diff-1);
            mOffset -= diff;
            mSize += diff;
            endRemoveRows();
            endRemoveColumns();

            beginRemoveRows   (QModelIndex(), mSize+diff, mSize-1);
            beginRemoveColumns(QModelIndex(), mSize+diff, mSize-1);
            mSize += diff;
            endRemoveRows();
            endRemoveColumns();
        }
        emit radiusChanged(newRadius);
    }
}

void EditableMask::setNorm(int norma)
{
    if (norma == 0) {
        norma = defaultNorm();
        if (norma == 0)
            norma = 1;
    }
    if (norma != mNorma) {
        mNorma = norma;
        emit normChanged(norma);
    }
}

void EditableMask::setDefaultNorm()
{
    setNorm(0);
}

int EditableMask::defaultNorm() const
{
    int sum = 0;
    for(int i=0; i<mSize; ++i)
    for(int j=0; j<mSize; ++j)
        sum += mData[mOffset+i][mOffset+j];
    return sum;
}

Tę maskę można edytować, powiększać pomniejszać


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 4x, ostatnio: MarekR22
K4
dzięki wielkie :)
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)