Форум программистов, компьютерный форум, киберфорум
Delphi для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.86/7: Рейтинг темы: голосов - 7, средняя оценка - 4.86
-232 / 5 / 0
Регистрация: 09.12.2022
Сообщений: 317

Картинка в потоке

07.09.2024, 07:54. Показов 1913. Ответов 24
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Помогите, люди добрые! Всю ночь провозился с одной штукой, так и не понял, в чем дело.
Есть форма, на ней вызывается объект, который запускает несколько потоков (можно один для начала).
При создании в объект передается картинка.
Delphi
1
2
3
4
5
procedure TinitForm.BitBtn1Click(Sender: TObject);
begin
th:=Tengine.create(fPicture, progrBar);
     th.OnFinish:=PassMap;
end;
это объект
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const PROC=1;
type
TMap=array of array of TColor;
 
  Tengine = class
  private
    fPicture: TBitmap;
    fMap: TMap;
    fbar: TProgressBar;
    FOnFinish: TNotifyEvent;
    procedure startMap;
    procedure GetMap(Sender: TObject); //<==starts when thread terminates
  public
    property OnFinish: TNotifyEvent read FOnFinish write FOnFinish;
    constructor create(Picture: TBitmap; var bar: TProgressbar);
    destructor destroy;
    property Map: TMap read fMap;
  end;
 
implementation
uses Initunit, MiniThread;
var
miniTh: array [1..PROC] of TMiniTh;
    k:byte;
{ TengineTh }
 
procedure Tengine.GetMap(Sender: TObject);
begin
    fmap:=AMap;
    if assigned(FOnFinish) then FOnFinish(self);
end;
 
constructor Tengine.create(Picture: TBitmap; var bar: TProgressBar);
begin
   //inherited Create(false);
   fpicture:=Picture;
   fbar:=bar;
   startMap; //<== start
end;
 
destructor Tengine.destroy;
begin
  fColCont.Destroy;
end;
 
procedure Tengine.startMap;
begin
  if ((FPicture.Width div fColCount=0) or (FPicture.Height div fRowCount=0)) then
  begin
    showmessage('Too small picture!');
    exit;
  end;
  //===============
  fBar.Max:=frowCount;
  //===============
  for k := 1 to PROC do    //threads starts immidiatelly
    minith[k]:=TMinith.create(Fpicture, 10, 20, fbar, k, GetMap); //<==здесь запускаются потоки
end;
 
end.
Это поток
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
type
TminiTh = class(TThread)
     fPicture: TBitmap;
     fMap: TMap;
     fColCount, fRowCount: integer;
     FthNum: byte;
     fComplete: boolean;
     fbar: TProgressBar;
     procedure GetMap;
     procedure IncBar;
  public
     property complete: boolean read fComplete;
     property Map: TMap read fMap;
     constructor create(Picture: TBitmap; ColCount, RowCount: integer;
     var bar: TProgressBar;
     thNum:byte;
     ATermProc: TNotifyEvent);overload;
  protected
     procedure Execute; override;
 
  end;
 
implementation
{ TminiTh }
 
constructor TminiTh.create(Picture: TBitmap;
ColCount, RowCount:integer; var bar: TprogressBar;
thnum:byte; ATermProc: TNotifyEvent);
begin
  inherited Create(false);
  fPicture:=Picture;
  fColcount:=Colcount;
  frowcount:=RowCount;
  fthnum:=thnum;
  fbar:=bar;
  OnTerminate:=ATermProc;
end;
 
procedure TminiTh.Execute;
begin
  self.getmap;      //<== 2) synchronize(getmap)
end;
 
procedure TminiTh.GetMap;
var i,j:integer;
 MyRect:TBitmap;
  WCoef, HCoef, u, v: word;
   sR, sG, sB, vRGB: longint;
begin
 
  SetLength(FMap, fRowCount div PROC,fColCount);
  //FPicture.SaveToFile('pic.bmp');  <==5)
  WCoef:=FPicture.Width div fColCount;
  HCoef:=FPicture.Height div fRowCount;
  myRect:=TBitmap.Create(WCoef, HCoef);
  for I := 0 to (fRowCount div PROC)*fthnum-1 do
  begin
    for j := 0 to fColCount-1 do
      begin
         myRect.Canvas.CopyRect(Rect(0,0,myRect.Width,MyRect.Height), <== 3)
                                FPicture.Canvas,
                                Rect(j*Wcoef,i*Hcoef,(j+1)*Wcoef,(i+1)*Hcoef  )); 
         sR:=0; sG:=0; sB:=0;
         //myRect.SaveToFile('f'+inttostr(i)+inttostr(j)+'.bmp');
         for u := 0 to myRect.Height-1 do
         for v := 0 to Myrect.Width-1 do
         begin
            vRGB:=ColorToRGB(MyRect.Canvas.Pixels[v,u]);  <--1) fPicture.Canvas.Pixels[i,j]=-1
            inc(sR,GetRValue(vRGB));
            inc(sG,GetGValue(vRGB));
            inc(sB,GetBValue(vRGB));
         end;
         //FPicture.SaveToFile('pic.bmp');    //<== 4)
        FMap[i,j]:=RGB(sr div (u*v), sg div (u*v), sb div (u*v));
 
      end;
     Synchronize(IncBar);
  end;
end;
 
procedure TminiTh.IncBar;
begin
   fbar.Position:=fbar.Position+1;
end;
Тут много текста, но суть вопроса проста: по запуску потока ему передается картинка. Эту картинку нужно разбить на одинаковые фрагменты (здесь 10X20) и усреднить пиксели. Так вот, неожиданно, всегда в разное время, но обычно на шаге 10м вдруг картинка перестает распознаваться (помечено стрелкой 1)) примерно в этом месте, но не обязательно именно здесь. Ну то есть пиксель видится, а потом резко становится -1. Если запускать getmap с помощью synchronize (помечено стрелкой 2)), то всё работает, но пропадает смысл работы в потоках. Разбивка на фрагменты (стрелка 3)) тоже не работает, хотя если сохранять картинку в файл, то она сохраняется. Если поместить сохранение картинки на место стрелки 4), то пиксели не теряются, но разбивка (стрелка 3) все равно не работает. Если на месте 5), то ничего не работает, но картинка сохраняется. Если кто сталкивался и понял механизм, может подскажете, что где можно починить? Извиняюсь за много текста, но я не знаю, как сократить.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
07.09.2024, 07:54
Ответы с готовыми решениями:

Управление потоками. Изменение значения label (созданного в потоке 1) в потоке 2
Код таков: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; ...

Как получить значение переменной созданной в потоке, в другом потоке?
Добрый день :) Возник такой вопрос &quot;как получить значение переменной созданной в потоке, в другом потоке?&quot; Не знаю, как правильно...

Работа с Dictionary в одном потоке, при этом он может изменятся в другом потоке
Здравствуйте! Я делаю лабу сервер распределенных вычислений в сети. В одном потоке ожидаю клиентов и на каждого клиента создаю поток. ...

24
631 / 526 / 104
Регистрация: 05.08.2022
Сообщений: 2,810
10.09.2024, 19:31
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от AzAtom Посмотреть сообщение
Только нужно гарантировать, что адрес начала битов не поменяется во время работы потоков.
Если во все эти "если" очень хорошо вникнуть - то, разумеется, можно и не копировать ничего никуда.
Я писал про вариант "надёжно и не сильно вникая".

Добавлено через 15 минут
Цитата Сообщение от t232006 Посмотреть сообщение
Если вы заметили, там объект myRect создается непосредственно в потоке и копирует фрагмент картинки из заблокированного fPicture
Если вы посмотрите исходники VCL, то там не просто копирование "из".

Цитата Сообщение от t232006 Посмотреть сообщение
myRect.Canvas.CopyRect(Rect(0,0,myRect.W idth,MyRect.Height), <== 3)
FPicture.Canvas,
Rect(j*Wcoef,i*Hcoef,(j+1)*Wcoef,(i+1)*H coef ));
Метод CopyRect() у исходной FPicture.Canvas вызывает методы, меняющие состояние объекта FPicture.Canvas.
Так что это не просто "источник копирования" и вызывать CopyRect из разных потоков не безопасно для объекта-источника.

Вот если бы вы получили адрес памяти и копировали просто память, т.е. точно зная что вы делаете - то другое дело.


На примере D5:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
procedure TCanvas.CopyRect(const Dest: TRect; Canvas: TCanvas;
  const Source: TRect);
begin
  Changing;
  RequiredState([csHandleValid, csFontValid, csBrushValid]);
  Canvas.RequiredState([csHandleValid, csBrushValid]);        <----- !!
 
.....
 
procedure TCanvas.RequiredState(ReqState: TCanvasState);
var
  NeededState: TCanvasState;
begin
  NeededState := ReqState - State;
............
    if csFontValid in NeededState then CreateFont;
    if csPenValid in NeededState then CreatePen;
    if csBrushValid in NeededState then CreateBrush;                <----- !!
Вероятно вызывается метод CreateBrush().
Короче состояние объекта fPicture явно меняется, в вашем случае - многопоточно.
Хотя кажется, что вы "просто из него копируете". Да и результат "иногда начинает глючить" это явно подтверждает.
2
-232 / 5 / 0
Регистрация: 09.12.2022
Сообщений: 317
10.09.2024, 19:40  [ТС]
AzAtom, да, действительно, ваш алгоритм работает превосходно, если не забыть указать глубину. Если забыть, то замедляется в 10 раз по сравнению с моим. Только цвета искажает. Но я думаю, это излечимо.
0
Модератор
4153 / 2362 / 814
Регистрация: 15.11.2015
Сообщений: 9,495
10.09.2024, 21:32
Цитата Сообщение от t232006 Посмотреть сообщение
Если забыть, то замедляется в 10 раз по сравнению с моим.
Есть такое дело. Изначально PixelFormat равен pfDevice, что является DDB (Device Depended Bitmap) и может находиться в видеопамяти. А задание pf32bit, например, создаёт DIB (Device Independed Bitmap) и картинка находится в системной памяти. Чтобы получить адрес строки в первом случае вся картинка копируется в системную память, а во втором случае она уже в памяти.

Если адрес строки вычислять самостоятельно, а не вызывать ScanLine каждый раз, то скорость в данном случае можно повысить ещё раза в 1,5.
2
-232 / 5 / 0
Регистрация: 09.12.2022
Сообщений: 317
13.09.2024, 17:15  [ТС]
AzAtom, у вас превосходный алгоритм за тем исключением, что
pb[xx] отвечает за B
pb[xx+1] отвечает за G
pb[xx+2] отвечает за R
Таким образом, правильный алгоритм будет выглядеть следующим образом:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
procedure TminiTh.GetMap22;
var
  i,j, xx: integer;
  MyRect: TBitmap;
  WCoef, HCoef, u, v: word;
  sR, sG, sB, vRGB: longint;
  pb: pbytearray;
begin
  SetLength(FMap, fRowCount, fColCount);
  WCoef := fPicture.Width div fColCount;
  HCoef := fPicture.Height div fRowCount;
  fpicture.PixelFormat:=pf24bit;
  for I := 0 to fRowCount-1 do
  begin
    for j := 0 to fColCount-1 do
    begin
      sR:=0; sG:=0; sB:=0;
      for u := i*Hcoef to (i+1)*Hcoef-1 do
      begin
        pb := fPicture.ScanLine[u];
        for v := j*Wcoef to (j+1)*Wcoef-1 do 
        begin
          xx := v*3;
          Inc(sB, pb[xx  ]);
          Inc(sG, pb[xx+1]);
          Inc(sR, pb[xx+2]);
         end;
      end;
      FMap[i,j]:=RGB(sr div (Wcoef*Hcoef), sg div (Wcoef*Hcoef), sb div (Wcoef*Hcoef));
    end;
    Synchronize(IncBar);
  end;
end;
0
57 / 52 / 5
Регистрация: 18.11.2018
Сообщений: 295
21.09.2024, 02:03
Цитата Сообщение от KSergey9 Посмотреть сообщение
Вот если бы вы получили адрес памяти и копировали просто память, т.е. точно зная что вы делаете
Так если мы точно знаем что хотим и что нужно делать можно и писать в один битмап многопоточно прямой записью в память.
Просто некоторые условия потокам поставить.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
21.09.2024, 02:03

Создать приложение, в отдельном потоке вычисляющее значение w и непрерывно обновляющее его в потоке
Ребят, с потоками не работал не когда. Есть задание , я понимаю что хотят, а вот как сделать потоками хз . Создайте приложение, в...

Многопоточная программа: В первом потоке происходит суммирование чисел, а во втором потоке чтение из файла
Добрый день. Нужно составить программу на языке С# в которой работают параллельно два потока. В первом потоке происходит...

GDI+. Почему в отдельном потоке картинки сохраняются как белые прямоугольники, а в основном потоке - всё Ок?
Всем привет, делаю функцию уменьшения картинок при помощи библиотеки GDI+, в отдельном потоке происходит уменьшение картинки и её...

Не могу создать поток( Нужно чтобы функция запускается в потоке и работала там пока не закроем в потоке)
хм.. не могу создать поток в c++ и направить туда. Нужно чтобы функция запускается в потоке и работала там пока не закроем в потоке.

Элементы управления, созданные в одном потоке, не могут быть родительскими для элемента управления в другом потоке
Привет :) Есть задача - нужно динамически добавлять компоненты. Все работает хорошо. Но если добавлять через цикл то начинает зависать...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
25
Ответ Создать тему
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru