double CheckPatterns::countBlackWhiteTransisitionHorizontal(const ImageGray &sourceImage, int percentage) { //bt->reset(); //bt->start(); //int y = (int)(sourceImage.height() / 100.0 * percentage); int y = Extensions::getValueFromPercentage(sourceImage.height(), percentage); int pixelCounter = 0; // set oldpixel to 1 because white is 1 and every ImageGray starts with white int oldPixel = 1; auto end_ptr = sourceImage.data(0, y + 1); bool oldpixel = 1; for (auto pxl_ptr = sourceImage.data(0, y); pxl_ptr < end_ptr; pxl_ptr++) { bool currentPixel = (*pxl_ptr > thresholdValue); pixelCounter += (int)(currentPixel != oldPixel); oldPixel = currentPixel; } //bt->stop(); //std::cout << "Time for the checkBlackWhiteTransision function: " << //bt->elapsedMicroSeconds() << " Microseconds (" << //bt->elapsedMilliSeconds() << "ms)" << std::endl; //return pixelCounter; return Extensions::getWeightFromPercentage(Extensions::getPercentage(pixelCounter, percentage)); }
double CheckPatterns::countBlackPixelsPerRowHorizontal(const ImageGray &sourceImage, int percentage) { //bt->reset(); //bt->start(); if (percentage > 100) { percentage = 100; } else if (percentage < 0) { percentage = 0; } //int y = (int)(sourceImage.height() / 100.0 * percentage); int y = Extensions::getValueFromPercentage(sourceImage.height(), percentage); //int y = Extensions::getPercentage(percentage, sourceImage.height()); int blackPixels = 0; auto end_ptr = sourceImage.data(0, y + 1); for (auto pxl_ptr = sourceImage.data(0, y); pxl_ptr < end_ptr; pxl_ptr++) { blackPixels += (int)(*pxl_ptr < thresholdValue); } //bt->stop(); //std::cout << "Time for the countBlackPixelsPerRowHorizontal function: " << //bt->elapsedMicroSeconds() << " Microseconds (" << //bt->elapsedMilliSeconds() << "ms)" << std::endl; //return blackPixels; //return (int)(blackPixels / (double)(sourceImage.width()) * 100); //std::cout << "blackpixels: " << blackPixels << " sourceImg Width: " << sourceImage.width() << " percentage: " << Extensions::getPercentage(blackPixels, sourceImage.width()) << std::endl; return Extensions::getWeightFromPercentage(Extensions::getPercentage(blackPixels, sourceImage.width())); }
int extract_cc_(Pixel p, std::vector<Pixel> &cc, ImageGray<BYTE> &img) { std::stack<Pixel> s; BYTE *pColor; if (img.pixelInside(p.x, p.y)) pColor = &img.pixel(p.x, p.y); else return 0; while (*pColor == 0 || !s.empty()) { if (*pColor == 0) { cc.push_back(p); *pColor = 255; s.push(Pixel(p.x+1, p.y)); s.push(Pixel(p.x-1, p.y)); s.push(Pixel(p.x, p.y+1)); s.push(Pixel(p.x, p.y-1)); } p = s.top(); s.pop(); if (img.pixelInside(p.x, p.y)) pColor = &img.pixel(p.x, p.y); else *pColor = 255; } return cc.size(); }
ImageGray HistEq(const ImageGray& image) { std::vector<int> hist = Histogram(image); int histSum = 0; for (int i = 0; i < 256; ++i) histSum += hist[i]; int accumulateSum = 0; std::vector<GrayValue> map(256); for (int i = 0; i < 256; ++i) { accumulateSum += hist[i]; map[i] = static_cast<GrayValue>(accumulateSum * 255.0 / histSum); } // create new image and map color int width = image.GetWidth(); int height = image.GetHeight(); GrayValue* data = new GrayValue[width * height]; for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { data[y * width + x] = map[image.GetValue(x, y)]; } } ImageGray gray(width, height, data); delete[] data; return gray; }
std::vector<int> Histogram(const ImageGray& image) { int width = image.GetWidth(); int height = image.GetHeight(); std::vector<int> hist(256); for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { GrayValue v = image.GetValue(x, y); hist[v]++; } } return hist; }
SegmentList WaterShedDecomposer::decompose(ImageColor const & image) const { QTime time; time.start(); // original image image.save("WS1_original.png"); // filter image time.restart(); ImageColor filtered = filterGauss(image, radiusGauss->value()); qDebug("Image filtered in %g seconds", time.restart()/1000.0); filtered.save("WS2_filtered.png"); // calculate gradient magnitude map time.restart(); ImageGray gradientMap = gradientMagnitude(filtered); qDebug("Gradient magnitude map calculated in %g seconds", time.restart()/1000.0); gradientMap.save("WS3_gradient.png"); // apply watershed transformation time.restart(); SegmentList segments = watershed(gradientMap, image); qDebug("Watershed transformation applied in %g seconds", time.restart()/1000.0); qDebug(" Segments: %d", segments.size()); ImageColor debugOut(image.width(), image.height()); segments.copyToImageAVG(debugOut); debugOut.save("WS4_transformed.png"); // merge similiar and small segments time.restart(); int oldSegmentsSize; do { oldSegmentsSize = segments.size(); mergeSimiliarSegments(segments, epsilonMerge->value()*epsilonMerge->value()); mergeSmallSegments(segments, minSize->value()); } while (segments.size() != oldSegmentsSize); qDebug("Segments merged in %g seconds", time.restart()/1000.0); qDebug(" Segments: %d", segments.size()); segments.copyToImageAVG(debugOut); debugOut.save("WS5_merged.png"); return segments; }
int CheckPatterns::findLeftBlackPixel(const ImageGray &sourceImage) { int xValue = sourceImage.width(); for (int y = sourceImage.height(); y >= 0; y--) { for (int x = 0; x <= (sourceImage.width() / 2); x++) { if (*(sourceImage.data(x, y)) < thresholdValue) { if (x <= xValue){ xValue = x; x = 0; y--; } } } } return xValue; }
int CheckPatterns::findBottomBlackPixel(const ImageGray &sourceImage) { int yValue = 0; for (int x = sourceImage.width(); x >= 0; x--) { for (int y = sourceImage.height(); y >= (sourceImage.height() / 2); y--) { if (*(sourceImage.data(x, y)) < thresholdValue) { if (y >= yValue) { yValue = y; x--; y = 0; } } } } return yValue; };
void extract_CCStats(std::vector<Pixel> &cc, CCStats &stats, const ImageGray<BYTE> &img) { stats.nPoints = cc.size(); stats.perimeter = 0; double meanX = 0, meanY = 0; double minX = img.xsize(), minY = img.ysize(), maxX = 0, maxY = 0; for (int i = 0; i < cc.size(); i++) { meanX += cc[i].x; meanY += cc[i].y; if (cc[i].x > maxX) maxX = cc[i].x; if (cc[i].x < minX) minX = cc[i].x; if (cc[i].y > maxY) maxY = cc[i].y; if (cc[i].y < minY) minY = cc[i].y; stats.perimeter += white_neighbors(Pixel(cc[i].x, cc[i].y), img); } stats.centerX = meanX / cc.size(); stats.centerY = meanY / cc.size(); stats.radius1 = 0.5 * (maxX - minX); stats.radius2 = 0.5 * (maxY - minY); }
double CheckPatterns::percentageBlack(const ImageGray & sourceImage, int xleft, int ytop, int xright, int ybottom) { std::pair<int, int> topleft = { Extensions::getValueFromPercentage(sourceImage.width(), xleft), Extensions::getValueFromPercentage(sourceImage.height(), ytop), }; std::pair<int, int> bottomright = { Extensions::getValueFromPercentage(sourceImage.width(), xright), Extensions::getValueFromPercentage(sourceImage.height(), ybottom), }; std::pair<int, int> size = { bottomright.first - topleft.first, bottomright.second - topleft.second }; unsigned int numBlack = 0; auto pxl_ptr = sourceImage.data(topleft.first, topleft.second); for (int y = size.second; y > 0; --y) { for (int x = size.first; x > 0; --x) { numBlack += *pxl_ptr < thresholdValue; pxl_ptr++; } pxl_ptr = sourceImage.data(topleft.first, topleft.second + y); } return Extensions::getWeightFromPercentage(Extensions::getPercentage(numBlack, size.first * size.second)); }
double CheckPatterns::firstEdgeLocationLeft(const ImageGray & sourceImage, int percentage) { int y = Extensions::getValueFromPercentage(sourceImage.height(), percentage); // set oldpixel to 1 because white is 1 and every ImageGray starts with white auto end_ptr = sourceImage.data(sourceImage.width(), y); bool oldpixel = 1; int x = 0; for (auto pxl_ptr = sourceImage.data(0, y); pxl_ptr < end_ptr; pxl_ptr++) { bool currentPixel = (*pxl_ptr > thresholdValue); if (oldpixel != currentPixel) { return Extensions::getWeightFromPercentage(Extensions::getPercentage(x, sourceImage.width())); } oldpixel = currentPixel; x++; } return 1.0; }
double CheckPatterns::checkSymmetryVertical(const ImageGray &sourceImage, bool boundingBox) { //bt->reset(); //bt->start(); int numberOfBlackPixels = 0, symmetricBlackPixels = 0; //double percentageSymmetric; if (boundingBox) { int x1 = this->findLeftBlackPixel(sourceImage); int x2 = this->findRightBlackPixel(sourceImage); int y1 = this->findTopBlackPixel(sourceImage); int y2 = this->findBottomBlackPixel(sourceImage); for (int y = y2; y >= ((y2 - y1) / 2); y--) { for (int x = x2; x >= x1; x--) { if (*(sourceImage.data(x, y)) < thresholdValue) { numberOfBlackPixels++; if (*(sourceImage.data(x, sourceImage.height() - y)) < thresholdValue) { symmetricBlackPixels++; } } } } } else { for (int x = 0; x < sourceImage.width(); x++) { auto top_ptr = sourceImage.data(x, 0); for (int offset = sourceImage.height() - 1; offset > 0; offset -= 2) { bool topBlack = *top_ptr < thresholdValue; bool bottomBlack = *(top_ptr + offset) < thresholdValue; //numberOfBlackPixels += topBlack; bool bothBlack = topBlack == bottomBlack; symmetricBlackPixels += (int)bothBlack; top_ptr += sourceImage.width(); } } } //percentageSymmetric = ((double)symmetricBlackPixels / (double)numberOfBlackPixels)*100.0; //bt->stop(); //std::cout << "Time for the checkSymmetryVertical function: " << //bt->elapsedMicroSeconds() << " Microseconds (" << //bt->elapsedMilliSeconds() << "ms)" << std::endl; //return (int)percentageSymmetric; return Extensions::getWeightFromPercentage(Extensions::getPercentage(symmetricBlackPixels, sourceImage.size()/2)); }
void IOHelper::ReadPGMFile(char* filename, ImageGray& img) { //Read File FILE* file = fopen(filename, "rb+"); assert(file!=NULL); assert(fseek(file, 0, SEEK_END)==0); U32 length = ftell(file); assert(length>0); fseek(file, 0, SEEK_SET); U8* bufferRead = new U8[length]; MemoryClear(bufferRead, length); U32 len = fread(bufferRead, 1, length, file); assert(len==length); fclose(file); //Read Image itr_vision::FormatPGM FormatPGMObj; itr_vision::IFormat::ImageInfo imageInfo; assert(FormatPGMObj.GetInfo(bufferRead, length, imageInfo)==itr_vision::IFormat::Success); img.Allocate(imageInfo.Width, imageInfo.Height); assert(FormatPGMObj.ToImage(bufferRead,length,img)==itr_vision::IFormat::Success); delete[] bufferRead; }
bool CC(std::vector<CCStats> &ccstats, const ImageGray<BYTE> &imgbi, ImageRGB<BYTE> &imgFeedback) { ImageGray<BYTE> img_copy(imgbi); std::vector<Pixel> firstPixels; double meansize = 0; for (int i = 0; i < imgbi.xsize(); i++) { for (int j = 0; j < imgbi.ysize(); j++) { std::vector<Pixel> ccC; CCStats stats; int npix = extract_cc_(Pixel(i, j), ccC, img_copy); if (npix > 180) { extract_CCStats(ccC, stats, imgbi); double compactness = 4*PI*stats.nPoints / (stats.perimeter*stats.perimeter); // !!compactness < 1.3 is not a good limit! I changed to 1.5 -Leman if (std::min(stats.radius1, stats.radius2) > 8 && compactness < 1.5 && compactness > 0.7) { ccstats.push_back(stats); firstPixels.push_back(Pixel(i, j)); meansize += stats.nPoints; // draw Feedback for (int k = 0; k < ccC.size(); ++k) { Pixel p = ccC[k]; // black means detected imgFeedback.pixel_R(p.x, p.y) = 0; imgFeedback.pixel_G(p.x, p.y) = 0; imgFeedback.pixel_B(p.x, p.y) = 0; } } else { // draw Feedback for (int k = 0; k < ccC.size(); ++k) { Pixel p = ccC[k]; // red means it's not a circle imgFeedback.pixel_R(p.x, p.y) = 150; imgFeedback.pixel_G(p.x, p.y) = 0; imgFeedback.pixel_B(p.x, p.y) = 0; } } } else { // draw Feedback for (int k = 0; k < ccC.size(); ++k) { Pixel p = ccC[k]; // green means it's too small imgFeedback.pixel_R(p.x, p.y) = 0; imgFeedback.pixel_G(p.x, p.y) = 150; imgFeedback.pixel_B(p.x, p.y) = 0; } } } double percent = ((double)i / (double)imgbi.xsize())*100; if (!(i % (int)(0.2*imgbi.xsize()+1))) libMsg::cout<<(int)(percent+1)<<'%'<<libMsg::flush; else if (!(i % (int)(0.04*imgbi.xsize()+1))) libMsg::cout<<'.'<<libMsg::flush; } libMsg::cout<<libMsg::endl; if (ccstats.size() == 0) { libMsg::cout<<"Nothing interesting found in this image. Please check."; return false; } // retrieve min_size and max_size to build a size histogram int max_val = 0; int rad_thre = 7; for (int i = 0; i < ccstats.size(); i++) { if (ccstats[i].nPoints > max_val) max_val = ccstats[i].nPoints; } std::vector<int> hist(max_val); std::vector<std::stack<int> > hist_stack(max_val); // to keep indeces of all the circles for given size // run through all the sizes and build frequency histogram for (int i = 0; i < ccstats.size(); i++) { int val = ccstats[i].nPoints-1; hist[val]++; hist_stack[val].push(i); } meansize /= ccstats.size(); int commonsize = meansize; libMsg::cout<<"Average area of region: "<<meansize<<" pixels"<<libMsg::endl; libMsg::cout<<"Max area of region: "<<max_val<<" pixels"<<libMsg::endl; libMsg::cout<<"Region found before filter: [ "<<ccstats.size()<<" ]"<<libMsg::endl; /* * frequency * ^ * | * | * | | * | | | larger than zerogap * | | | so we ignore A and B * | <---> | ||||| | | <---------------> * | A | | ||||| || | | B * ----------------------------------------------------> size * 0 ^ 10000 * | * average size * negative <---- ----> positive * direction direction */ // collect the inliers in positive direction from commonsize idx int zerosgap = meansize/5; int count = 0; int flag = zerosgap; std::vector<int> inliers(ccstats.size()); while (flag != 0 && commonsize+count < hist.size()) { int hist_idx = commonsize + count; int onesizecircles = hist[hist_idx]; if (onesizecircles == 0) { flag--; } else { while (!hist_stack[hist_idx].empty()) { inliers[hist_stack[hist_idx].top()] = 1; // inliers.push(hist_stack[hist_idx].top()); hist_stack[hist_idx].pop(); } flag = zerosgap; } count++; } // collect the inliers in negative direction from commonsize idx count = -1; flag = zerosgap; while (flag != 0 && commonsize+count >= 0) { int hist_idx = commonsize + count; int onesizecircles = hist[hist_idx]; if (onesizecircles == 0) { flag--; } else { while (!hist_stack[hist_idx].empty()) { inliers[hist_stack[hist_idx].top()] = 1; hist_stack[hist_idx].pop(); } flag = zerosgap; } count--; } std::vector<CCStats> erasedCCStats; std::vector<int> outliersIdx; int idx = 0; while (idx < ccstats.size()) { if (inliers[idx] == 0) { erasedCCStats.push_back(ccstats[idx]); outliersIdx.push_back(idx); ccstats.erase(ccstats.begin() + idx); inliers.erase(inliers.begin() + idx); } else { idx++; } } libMsg::cout<<"Region found after filter: [ "<<ccstats.size()<<" ]"<<libMsg::endl; if (erasedCCStats.size() > 0) { libMsg::cout<<"Erased circles:"<<libMsg::endl; ImageGray<BYTE> img_copy2(imgbi); for (int i = 0; i < erasedCCStats.size(); ++i) { CCStats &stats = erasedCCStats[i]; libMsg::cout<<"circle "<<i<<libMsg::endl; libMsg::cout<<"\tarea: "<<stats.nPoints<<libMsg::endl; libMsg::cout<<"\tcenter: "<<stats.centerX<<", "<<stats.centerY<<libMsg::endl; int index = outliersIdx[i]; Pixel first = firstPixels[index]; std::vector<Pixel> ccC; extract_cc_(first, ccC, img_copy2); for (int k = 0; k < ccC.size(); ++k) { Pixel p = ccC[k]; // blue means filtered imgFeedback.pixel_R(p.x, p.y) = 0; imgFeedback.pixel_G(p.x, p.y) = 0; imgFeedback.pixel_B(p.x, p.y) = 150; } } } return true; }
int white_neighbors(const Pixel &p, const ImageGray<BYTE> &img) { int nn = 0; Pixel p1(p.x-1, p.y); if (p1.x >= 0 && p1.x < img.xsize() && p1.y >= 0 && p1.y < img.ysize()) { if (img.pixel(p1.x, p1.y) == 255) nn++; } else { nn++; } Pixel p2(p.x+1, p.y); if (p2.x >= 0 && p2.x < img.xsize() && p2.y >= 0 && p2.y < img.ysize()) { if (img.pixel(p2.x, p2.y) == 255) nn++; } else { nn++; } Pixel p3(p.x, p.y-1); if (p3.x >= 0 && p3.x < img.xsize() && p3.y >= 0 && p3.y < img.ysize()) { if (img.pixel(p3.x, p3.y) == 255) nn++; } else { nn++; } Pixel p4(p.x, p.y+1); if (p4.x >= 0 && p4.x < img.xsize() && p4.y >= 0 && p4.y < img.ysize()) { if (img.pixel(p4.x, p4.y) == 255) nn++; } else { nn++; } if (nn > 0) nn = 1; return nn; }
void saveImg(const ImageGray & img, const std::string filename) { CImg<unsigned char> cimg(img.data(), img.width(), img.height(), 1, 1); cimg.save(filename.c_str()); }
SegmentList WaterShedDecomposer::watershed(ImageGray const & gradient, ImageColor const & image) const { SegmentList segments; std::unique_ptr<int[]> labels(new int[image.area()]); for (int i=0; i<image.area(); ++i) labels[i] = -1; int offsets[]{-image.width(), -1, 1, image.width()}; QList<GradPixelRef> queue; for (int i=0; i<gradient.area(); ++i) { queue << GradPixelRef{gradient.at(i).l, i}; } std::sort(queue.begin(), queue.end(), lessThan); int label; int lastLabel = -1; int i, j; QList<int> neighLbls; Pixel * pixel; Segment * segment; foreach (GradPixelRef const & gradPix, queue) { i = gradPix.index; pixel = new Pixel(Position(i%image.width(), i/image.width()), image.at(i)); // gather neighbours neighLbls.clear(); for (int o=0; o<4; ++o) { j = i + offsets[o]; if (image.areNeighbours(i, j) && labels[j] > -1 && !neighLbls.contains(labels[j])) { neighLbls << labels[j]; } } // treat pixel according to neighbour count switch (neighLbls.size()) { case 0: // new marker labels[i] = ++lastLabel; segment = new Segment(); segment->addPixel(pixel); segments << segment; break; case 1: // add to basin label = neighLbls.first(); labels[i] = label; segments.at(label)->addPixel(pixel); break; default: // new watershed // add pixel the segment of the nearest neighbour in color double distMin = std::numeric_limits<double>::max(); double dist; int jMin = 0; for (int o=0; o<4; ++o) { j = i + offsets[o]; if (image.areNeighbours(i, j) && labels[j] > -1) { dist = (image.at(i)-image.at(j)).magnitudeSquared(); if (dist < distMin) { distMin = dist; jMin = j; } } } labels[i] = labels[jMin]; segments.at(labels[jMin])->addPixel(pixel); // beneighbour the segments for (int k=0; k<neighLbls.count()-1; ++k) { for (int l=k+1; l<neighLbls.count(); ++l) { segments.at(neighLbls.at(k))->addNeighbour(segments.at(neighLbls.at(l))); segments.at(neighLbls.at(l))->addNeighbour(segments.at(neighLbls.at(k))); } } break; } }