Smorodov 578 Report post Posted June 3, 2010 Открытый движок распознавания символов: http://jocr.sourceforge.net/ Share this post Link to post Share on other sites
andrew smirnov 0 Report post Posted June 3, 2010 Открытый движок распознавания символов: http://jocr.sourceforge.net/ а еще очень не плох tesseract от HP, кажется. сейчас выложен GOOGLE: http://code.google.com/p/tesseract-ocr/ правда, есть нюансы с русским языком Share this post Link to post Share on other sites
blackfxx 0 Report post Posted August 24, 2010 Необходимо распознать текст на однотонном фоне (шрифт стандартный, например, Times New Roman) с определенной области изображения. За счет каких функций, решений это можно осуществить? Заранее спасибо. Share this post Link to post Share on other sites
ok_demo 0 Report post Posted August 24, 2010 Необходимо распознать текст на однотонном фоне (шрифт стандартный, например, Times New Roman) с определенной области изображения. За счет каких функций, решений это можно осуществить? Заранее спасибо. Думаю, так просто, как Вы думаете, не получится. Более того, OpenCV, ИМХО, не обладает таким потенциалом в области OCR, как специализированные разработки. Сталкивался с tesseract - вполне себе понравилось, и лицензия хорошая. Share this post Link to post Share on other sites
andrew smirnov 0 Report post Posted August 24, 2010 Думаю, так просто, как Вы думаете, не получится. Более того, OpenCV, ИМХО, не обладает таким потенциалом в области OCR, как специализированные разработки. Сталкивался с tesseract - вполне себе понравилось, и лицензия хорошая. tesseract неплох, это правда. еще понравились реализации подобных проблем с использованием нейронных сетей. обучается, правда, долго, но результат - как раз под описание проблемы Share this post Link to post Share on other sites
CornersKir 0 Report post Posted August 25, 2010 Необходимо распознать текст на однотонном фоне (шрифт стандартный, например, Times New Roman) с определенной области изображения. За счет каких функций, решений это можно осуществить? Заранее спасибо. Можно использовать CuneiForm. Автоматизация через COM-сервер Puma. Лицензия тоже хорошая. Share this post Link to post Share on other sites
blackfxx 0 Report post Posted August 25, 2010 Спасибо всем за предложенные советы. Попробовал tesseract - взял с GoogleCode библиотеку, стандартную дату для распознавания английского текста, попробовал. На шрифтах размера больше 14го работает замечательно. Но в моем случае шрифт приблизительно 8-9. Его в упор не видит. Пробовал увеличивать изображение 2-3 раза и распознавать - обнаруживает, что есть текст, но выдает полную кашу. Попробовал MODI(Microsoft Office Document Imaging). Распознает мелкий текст нормально - цифры без ошибок, а вот сочетания букв fd fl коряво. Буду пробовать другие библиотеки. Share this post Link to post Share on other sites
blackfxx 0 Report post Posted August 25, 2010 tesseract неплох, это правда. еще понравились реализации подобных проблем с использованием нейронных сетей. обучается, правда, долго, но результат - как раз под описание проблемы Если не сложно, не могли бы дать ссылку на пример с использованием нейронных сетей? Share this post Link to post Share on other sites
andrew smirnov 0 Report post Posted August 25, 2010 Если не сложно, не могли бы дать ссылку на пример с использованием нейронных сетей? Вот, если попробовать только http://xpidea.com/Products/tabid/53/Produc...10/Default.aspx а так google в помощь - попадались реализации Share this post Link to post Share on other sites
FingerScan 0 Report post Posted September 5, 2010 Насколько я помню в номерном знаке могут присутствовать цифры от 0 до 9 и буквы(А, В, Е, К, М, Н, О, Р, С, Т, У, Х). Успехов! Share this post Link to post Share on other sites
ok_demo 0 Report post Posted September 6, 2010 Успехов! Формат номерных автомобильных номерных знаков, а также перечень возможных символов, шрифт и т.д. определяется актами страны. Например, стандарт Беларуси: http://www.gosstandart.gov.by/txt/BDD_pdf/914.pdf Share this post Link to post Share on other sites
FingerScan 0 Report post Posted September 6, 2010 Формат номерных автомобильных номерных знаков, а также перечень возможных символов, шрифт и т.д. определяется актами страны. Да, безусловно. Но то, что реально ездит по дорогам данной страны этими актами не определяется. Какое дело иностранцу на автомобиле (из той же России, Украины) до внутренних актов, по которым Белорусия выпускает свои номера? Я лично на улицах своего города вижу и российские, и украинские, и европейские, и именные, и транзитные, и подарочные, и какие только хочешь номера ... Можно конечно закрыть глаза руками и сделать вид, что эти машины не ездят по улицам транзитом на вполне законных основаниях. Но нужно ли? Share this post Link to post Share on other sites
anton 0 Report post Posted December 10, 2010 Да, я читал форум и схожую тему, но ничего конечного не нашел, только наброски и предположения. В связи с этим создаю новую тему, результатами которой станет законченный код от получения картинки с камеры до текста с автомобильного номера. Суть идеи пока такая: 1. Получаем картинку с камеры. 2. Определяем наличие движения. 3. Когда присутствует движение - выхватываем его и ищем на нём автомобильные номера. 4. Вырезаем этот самый номер. 5. Распознаем номер. Собственно, подскажите пожалуйста, где я прав или не прав, какие функции можно дёргать.... спасибо за любую помощь! Для начала есть (взято с примеров) - получение изображения с камеры и детектирование движения: #include "cv.h" #include "highgui.h" #include <time.h> #include <math.h> #include <ctype.h> #include <stdio.h> // various tracking parameters (in seconds) const double MHI_DURATION = 1; const double MAX_TIME_DELTA = 0.5; const double MIN_TIME_DELTA = 0.05; // number of cyclic frame buffer used for motion detection // (should, probably, depend on FPS) const int N = 5; // ring image buffer IplImage **buf = 0; int last = 0; // temporary images IplImage *mhi = 0; // MHI IplImage *orient = 0; // orientation IplImage *mask = 0; // valid orientation mask IplImage *segmask = 0; // motion segmentation map CvMemStorage* storage = 0; // temporary storage // parameters: // img – input video frame // dst – resultant motion picture // args – optional parameters void update_mhi( IplImage* img, IplImage* dst, int diff_threshold ) { double timestamp = (double)clock()/CLOCKS_PER_SEC; // get current time in seconds CvSize size = cvSize(img->width,img->height); // get current frame size int i, idx1 = last, idx2; IplImage* silh; CvSeq* seq; CvRect comp_rect; double count; double angle; double magnitude; CvScalar color; // allocate images at the beginning or // reallocate them if the frame size is changed if( !mhi || mhi->width != size.width || mhi->height != size.height ) { if( buf == 0 ) { buf = (IplImage**)malloc(N*sizeof(buf[0])); memset( buf, 0, N*sizeof(buf[0])); } for( i = 0; i < N; i++ ) { cvReleaseImage( &buf[i] ); buf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 ); cvZero( buf[i] ); } cvReleaseImage( &mhi ); cvReleaseImage( &orient ); cvReleaseImage( &segmask ); cvReleaseImage( &mask ); mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 ); cvZero( mhi ); // clear MHI at the beginning orient = cvCreateImage( size, IPL_DEPTH_32F, 1 ); segmask = cvCreateImage( size, IPL_DEPTH_32F, 1 ); mask = cvCreateImage( size, IPL_DEPTH_8U, 1 ); } cvCvtColor( img, buf[last], CV_BGR2GRAY ); // convert frame to grayscale idx2 = (last + 1) % N; // index of (last – (N-1))th frame last = idx2; silh = buf[idx2]; cvAbsDiff( buf[idx1], buf[idx2], silh ); // get difference between frames cvThreshold( silh, silh, diff_threshold, 1, CV_THRESH_BINARY ); // and threshold it cvUpdateMotionHistory( silh, mhi, timestamp, MHI_DURATION ); // update MHI // convert MHI to blue 8u image cvCvtScale( mhi, mask, 255./MHI_DURATION, (MHI_DURATION - timestamp)*255./MHI_DURATION ); cvZero( dst ); cvCvtPlaneToPix( mask, 0, 0, 0, dst ); // calculate motion gradient orientation and valid orientation mask cvCalcMotionGradient( mhi, mask, orient, MAX_TIME_DELTA, MIN_TIME_DELTA, 3 ); if( !storage ) storage = cvCreateMemStorage(0); else cvClearMemStorage(storage); // segment motion: get sequence of motion components // segmask is marked motion components map. It is not used further seq = cvSegmentMotion( mhi, segmask, storage, timestamp, MAX_TIME_DELTA ); // iterate through the motion components, // One more iteration (i == -1) corresponds to the whole image (global motion) for( i = -1; i < seq->total; i++ ) { if( i < 0 ) { // case of the whole image comp_rect = cvRect( 0, 0, size.width, size.height ); color = CV_RGB(255,255,255); magnitude = 100; } else { // i-th motion component comp_rect = ((CvConnectedComp*)cvGetSeqElem( seq, i ))->rect; if( comp_rect.width + comp_rect.height < 100 ) // reject very small components continue; color = CV_RGB(255,0,0); magnitude = 30; } // select componen ROI cvSetImageROI( silh, comp_rect ); cvSetImageROI( mhi, comp_rect ); cvSetImageROI( orient, comp_rect ); cvSetImageROI( mask, comp_rect ); // calculate orientation angle = cvCalcGlobalOrientation( orient, mask, mhi, timestamp, MHI_DURATION); angle = 360.0 - angle; // adjust for images with top-left origin count = cvNorm( silh, 0, CV_L1, 0 ); // calculate number of points within silhouette ROI cvResetImageROI( mhi ); cvResetImageROI( orient ); cvResetImageROI( mask ); cvResetImageROI( silh ); // check for the case of little motion if( count < comp_rect.width*comp_rect.height * 0.05 ) continue; } } int main(int argc, char** argv) { IplImage* motion = 0; CvCapture* capture = 0; if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0]))) capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 ); else if( argc == 2 ) capture = cvCaptureFromFile( argv[1] ); if( capture ) { cvNamedWindow( "Motion", 1 ); cvNamedWindow( "Original", 2 ); for(; { IplImage* image; if( !cvGrabFrame( capture )) break; image = cvRetrieveFrame( capture ); if( image ) { if( !motion ) { motion = cvCreateImage( cvSize(image->width,image->height), 8, 3 ); cvZero( motion ); motion->origin = image->origin; } } update_mhi( image, motion, 30 ); cvShowImage( "Original", image ); cvShowImage( "Motion", motion ); // cvAbsDiff(); if( cvWaitKey(10) >= 0 ) break; } cvReleaseCapture( &capture ); cvDestroyAllWindows(); } return 0; } [/code] Вопрос первый, как по полученному движению - вырезать область движения (прямоугольную) из оригинального изображения? Вопрос второй, как искать номер? - Каскады Хаара, наверное слишком для этого? Share this post Link to post Share on other sites
Smorodov 578 Report post Posted December 11, 2010 Очень уж крупноблочно не вызывает вопросов только первый и четвертый пункт. По остальным есть такие соображения: 2: движение в кадре лучше определять не шаблонами движения, а хотя бы как в примерах bgfg_codebook или bgfg_segm. 3: ищем номера - тоже большая функция с множеством проблем, таких как грязные номера, блики, различный шрифт, различное расположение номеров на разных автомобилях. При идеальной бинаризации можно попробовать искать прямоугольник, как в примерах (squares.c). Перед тем, как определять прямоугольник нужно исправить перспективу, чтобы на изображении был прямоугольник а не ромб или что то в этом роде (гомография). При неидеальной можно использовать преобразование Хафа (Hough) для прямоугольников, но его придется делать вручную, такая ф-ция не встроена в opencv. 5: Для распознавания есть спец. библиотеки с открытым кодом (ссылки на них есть на форуме). Share this post Link to post Share on other sites
anton 0 Report post Posted December 11, 2010 Уже успел встроить в код поиск прямоугольников из вышеупомянутого примера!... Работает конечно кривова-то: находит всё, что ни попадя... И бинаризация, кстати, хорошая идея для решения части из проблем. Пока решил попробовать, с определённой частотой, например 6 кадров в секунду, брать изображение и искать на нём прямоугольники... Тут отпадёт необходимость поиска движения! И если есть прямоугольники - то пытаться их распознавать, при этом их отфильтровав: Очевидно, что их верхняя и нижняя стороны примерно равны, тоже и с правой - левой, а боковинки будут меньше, чем верхушки.... Коечно в расчёт берётся то, что номер мы получаем без искажений. Маленькое уточнение, это учебный проект, в конце которого необходимо иметь рабочий прототип, который потом уже можно будет оптимизировать, так что пока - что грязь, искажения и прочие трудности в расчёт не беруться. Есть встроеные функции для бинаризации? Код после небольшой оптимизации выложу для желающих. Share this post Link to post Share on other sites
Smorodov 578 Report post Posted December 11, 2010 cvThreshold,cvAdaptiveThreshold. Share this post Link to post Share on other sites
anton 0 Report post Posted December 12, 2010 Вот, наваял! - берёт изображение с камеры - бинаризует, ищет прямоугольники... крайне нехорошо ищет, подтормаживает и утекает память! Кто знает, как бороться? #ifdef _CH_ #pragma package <opencv> #endif #define CV_NO_BACKWARD_COMPATIBILITY #include "cv.h" #include "highgui.h" #include <time.h> #include <math.h> #include <ctype.h> #include <stdio.h> #include <string.h> int thresh = 50; IplImage* img = 0; IplImage* img0 = 0; CvMemStorage* storage = 0; const char* wndname = "Square Detection"; double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 ) { double dx1 = pt1->x - pt0->x; double dy1 = pt1->y - pt0->y; double dx2 = pt2->x - pt0->x; double dy2 = pt2->y - pt0->y; return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); } CvSeq* findSquares4( IplImage* img, CvMemStorage* storage ) { CvSeq* contours; int i, c, l, N = 11; CvSize sz = cvSize( img->width & -2, img->height & -2 ); IplImage* timg = cvCloneImage( img ); // make a copy of input image IplImage* gray = cvCreateImage( sz, 8, 1 ); IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 ); IplImage* tgray; CvSeq* result; double s, t; // create empty sequence that will contain points - // 4 points per square (the square's vertices) CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage ); // select the maximum ROI in the image // with the width and height divisible by 2 cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height )); // down-scale and upscale the image to filter out the noise cvPyrDown( timg, pyr, 7 ); cvPyrUp( pyr, timg, 7 ); tgray = cvCreateImage( sz, 8, 1 ); // find squares in every color plane of the image for( c = 0; c < 3; c++ ) { // extract the c-th color plane cvSetImageCOI( timg, c+1 ); cvCopy( timg, tgray, 0 ); // try several threshold levels for( l = 0; l < N; l++ ) { // hack: use Canny instead of zero threshold level. // Canny helps to catch squares with gradient shading if( l == 0 ) { // apply Canny. Take the upper threshold from slider // and set the lower to 0 (which forces edges merging) cvCanny( tgray, gray, 0, thresh, 5 ); // dilate canny output to remove potential // holes between edge segments cvDilate( gray, gray, 0, 1 ); } else { // apply threshold if l!=0: // tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY ); } // find contours and store them all as a list cvFindContours( gray, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) ); // test each contour while( contours ) { // approximate contour with accuracy proportional // to the contour perimeter result = cvApproxPoly( contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 ); // square contours should have 4 vertices after approximation // relatively large area (to filter out noisy contours) // and be convex. // Note: absolute value of an area is used because // area may be positive or negative - in accordance with the // contour orientation if( result->total == 4 && cvContourArea(result,CV_WHOLE_SEQ,0) > 1000 && cvCheckContourConvexity(result) ) { s = 0; for( i = 0; i < 5; i++ ) { // find minimum angle between joint // edges (maximum of cosine) if( i >= 2 ) { t = fabs(angle( (CvPoint*)cvGetSeqElem( result, i ), (CvPoint*)cvGetSeqElem( result, i-2 ), (CvPoint*)cvGetSeqElem( result, i-1 ))); s = s > t ? s : t; } } // if cosines of all angles are small // (all angles are ~90 degree) then write quandrange // vertices to resultant sequence if( s < 0.3 ) for( i = 0; i < 4; i++ ) cvSeqPush( squares, (CvPoint*)cvGetSeqElem( result, i )); } // take the next contour contours = contours->h_next; } } } // release all the temporary images cvReleaseImage( &gray ); cvReleaseImage( &pyr ); cvReleaseImage( &tgray ); cvReleaseImage( &timg ); return squares; } void drawSquares( IplImage* img, CvSeq* squares ) { CvSeqReader reader; IplImage* cpy = cvCloneImage( img ); int i; // initialize reader of the sequence cvStartReadSeq( squares, &reader, 0 ); // read 4 sequence elements at a time (all vertices of a square) for( i = 0; i < squares->total; i += 4 ) { CvPoint pt[4], *rect = pt; int count = 4; // read 4 vertices CV_READ_SEQ_ELEM( pt[0], reader ); CV_READ_SEQ_ELEM( pt[1], reader ); CV_READ_SEQ_ELEM( pt[2], reader ); CV_READ_SEQ_ELEM( pt[3], reader ); // draw the square as a closed polyline cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 ); } // show the resultant image cvShowImage( wndname, cpy ); cvReleaseImage( &cpy ); } IplImage* frame = 0; IplImage* imgForSquare = 0; IplImage* imgGray = 0; IplImage* imgBinary = 0; int main() { storage = cvCreateMemStorage(0); cvNamedWindow("original",CV_WINDOW_AUTOSIZE); //cvNamedWindow("gray",CV_WINDOW_AUTOSIZE); cvNamedWindow("binary",CV_WINDOW_AUTOSIZE); CvCapture* capture = cvCreateCameraCapture(CV_CAP_ANY); while(1) { frame = cvQueryFrame( capture ); imgGray = cvCreateImage(cvGetSize(frame), frame->depth, 1); cvConvertImage(frame, imgGray, CV_BGR2GRAY); imgBinary = cvCreateImage( cvGetSize(imgGray), IPL_DEPTH_8U, 1); //cvAdaptiveThreshold(imgGray, imgBinary, 50, 250, CV_THRESH_BINARY); cvAdaptiveThreshold(imgGray, imgBinary, 250, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 5, 3); if( !frame ) { break; } drawSquares( frame, findSquares4( frame, storage ) ); cvShowImage( "original", frame ); //cvShowImage( "gray", imgGray ); cvShowImage( "binary", imgBinary); //cvReleaseImage(&frame); cvReleaseImage(&imgGray); cvReleaseImage(&imgBinary); // drawSquares( imgGray, findSquares4( imgGray, storage ) ); char c = cvWaitKey(33); if (c == 27) { break; } } cvClearMemStorage( storage ); cvReleaseCapture( &capture ); ///cvReleaseImage(&frame); cvReleaseImage(&imgGray); cvReleaseImage(&imgBinary); cvDestroyAllWindows(); return 0; } Share this post Link to post Share on other sites
nagoHok 4 Report post Posted June 29, 2011 Очень заинтересовался проектом, точнее вплотную принялся за подобную разработку. Код выложенный anton - м действительно очень тормозной, и не всегда коректно находит прямоугольники. Утечка памяти изза того что не чистит MemStorage. Перерыл море инфы на ткнулся вот на подобный алгоритм http://cms.tusur.ru/filearchive/reports-magazine/2008-2-1/34-39.pdf заключается он в разбиение изображения на блоки и составления карты контрастности Кто что может сказать про данный метод? Share this post Link to post Share on other sites
nagoHok 4 Report post Posted July 13, 2011 Итак перерыв форум, наткнулся на ссылочку http://www.inf.tsu.ru/library/DiplomaWorks/CompScience/2006/busigin/diplom.pdf Очень понравился алгоритм про сканирование изображения в горизонтальном сечении и построения графика амплитуды яркости от координат. Единственное на чем встал это на предложеной функции в разделе 3,3 на 21 странице, ни как не получается получить похожий график, к томуже далее предлагается получить производные от этой функции, но так как вуз мною закончен более 10 лет назад, вся основа вышки благополучно забыта. Если кто силен в математике, подскажите что делать. Спасибо за рание. int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { cvNamedWindow("Org"); cvNamedWindow("Gray"); IplImage* frame = NULL; frame = cvLoadImage("c:/O807MK177.jpg"); IplImage* gray = NULL; gray = cvCreateImage(cvGetSize(frame), 8, 1); cvConvertImage(frame, gray, CV_BGR2GRAY); CvPoint ptLast = cvPoint(0, 0); DWORD dwStart = GetTickCount(); int nLastYPos = 0; for (int nY = 0; nY < gray->height; nY+=10) { ptLast = cvPoint(0, nY); for (int nX = 0; nX < gray->widthStep - 2; nX++) { int nBr = gray->imageData[nY * gray->widthStep + nX] / 10; int nBr1 = gray->imageData[nY * gray->widthStep + nX + 1] / 10; nBr = abs(nBr1 - nBr); cvLine(frame, ptLast, cvPoint(nX, nY - nBr) , cvScalar(0), 1); ptLast = cvPoint(nX, nY - nBr); } } dwStart = GetTickCount() - dwStart; std::cout << dwStart; cvShowImage("Gray", gray); cvShowImage("Org", frame); cvReleaseImage(&gray); cvReleaseImage(&frame); gray = NULL; frame = NULL; char c = cvWaitKey(0); if (c == 27) { return 0; } return 0; } Share this post Link to post Share on other sites
mrgloom 242 Report post Posted July 13, 2011 ну производную, то в дискретном случае можно взять как (y2-y1)/(x2-x1). как сглаживать и зачем не знаю. Share this post Link to post Share on other sites
Nuzhny 243 Report post Posted July 13, 2011 Вторая производная: D2 y(i) = y(i - 1) - 2 * y(i) + y(i + 1) Share this post Link to post Share on other sites
mrgloom 242 Report post Posted July 13, 2011 сглаживать оказывается надо чтобы производная была непрерывная. http://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%B0%D0%B4%D0%BA%D0%B0%D1%8F_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F http://forum2007.algolist.ru/showflat.php/Cat/0/Number/760/an/0/page/15 Share this post Link to post Share on other sites
nagoHok 4 Report post Posted July 13, 2011 Спасибо, буду изучать вопрос. Share this post Link to post Share on other sites
Smorodov 578 Report post Posted July 13, 2011 Смысл основной функции (от которой берется производная) заключается в суммировании переходов яркости. Так как на номерном знаке таких переходов много (буквы на белом фоне) и они резкие (что тоже учитывается), то функция, при проходе линии сканирования по номерному знаку будет на месте знака возрастать. Возрастать она будет, естественно, достаточно негладко (будет высокочастотная составляющая). Для того чтобы далее работать с этой функцией нам нужно устранить высокочастотную составляющую, т.к. она будет давать огромные значения шума при вычислении производной. То есть сгладить функцию например при помощи скользящего среднего (можно кумулятивного) с выборкой сопоставимой с размерами номерного знака (надо подбирать). И уже от сглаженной функции брать производные, тогда все получится Share this post Link to post Share on other sites
nagoHok 4 Report post Posted July 13, 2011 А подойдет ли следующие сглаживание: к примеру беру пять точек и высчитываю среднее значение между ними? т.е. -2 -1 0 1 2, и ставлю полученнное значение на место 0-го элемента? Share this post Link to post Share on other sites