bool consensus_skeleton(vector<NeuronTree> & nt_list, QList<NeuronSWC> & merge_result, int method_code,V3DPluginCallback2 &callback) { double CLUSTERING_RANGE =10; //potentially, there are invalid neuron trees (massive node points, no node points, looping) remove_outliers(nt_list); int neuronNum = nt_list.size(); //initialize the image volume to record/accumulate the location votes from neurons MyBoundingBox bbUnion = neuron_trees_bb(nt_list); Point3D offset = {bbUnion.min_x ,bbUnion.min_y ,bbUnion.min_z }; float closeness = 1.0; V3DLONG sz_x = ceil((bbUnion.max_x - bbUnion.min_x ) / closeness) +1; //+0.5 to round up from float to V3DLONG V3DLONG sz_y = ceil((bbUnion.max_y - bbUnion.min_y ) / closeness) +1; V3DLONG sz_z = ceil((bbUnion.max_z - bbUnion.min_z ) / closeness) +1; V3DLONG tol_sz = sz_x * sz_y * sz_z; cout << "image size = " << tol_sz<<": " <<sz_x<<"x "<<sz_y<<" x"<<sz_z<< endl; unsigned char * img1d = new unsigned char[tol_sz]; for(V3DLONG i = 0; i < tol_sz; i++) img1d[i] = 0; //count for (int j =0; j < nt_list.size(); j++){ NeuronTree nt = nt_list[j]; for (int i =0; i < nt.listNeuron.size(); i++) { NeuronSWC node = nt.listNeuron.at(i); V3DLONG id_x = (node.x-offset.x) +0.5; //round up V3DLONG id_y = (node.y-offset.y) +0.5; V3DLONG id_z = (node.z-offset.z) +0.5; V3DLONG idx = id_z * (sz_x*sz_y) + id_y * sz_x + id_x; if (idx <tol_sz ){ img1d[idx] ++ ;} else{ cout <<"error idx" <<endl; } } } //for debug only Image4DSimple *image = new Image4DSimple(); image->setData(img1d, sz_x, sz_y, sz_z, 1, V3D_UINT8); callback.saveImage(image, "./vote_count_image.v3draw"); //non-maximum suppresion vector<Point3D> node_list; vector<unsigned int> vote_list; non_max_suppresion (img1d,sz_x,sz_y,sz_z,offset,node_list,vote_list,3); cout << "After non_max supression:"<< endl; cout << "number of nodes:"<< node_list.size() << endl; cout << "maximum votes:" << v_min(vote_list) << endl; cout << "minimum votes:" << v_max(vote_list) << endl; Image4DSimple *image2 = new Image4DSimple(); image2->setData(img1d, sz_x, sz_y, sz_z, 1, V3D_UINT8); callback.saveImage(image2, "./nms_image.v3draw"); // for debug: save node_list to check locations QList<NeuronSWC> locationTree; for (int i=0;i<node_list.size();i++) { NeuronSWC tmp; tmp.x = node_list[i].x; tmp.y = node_list[i].y; tmp.z = node_list[i].z; tmp.type = 2; //edge votes tmp.pn = -1; //parent id, form the edge tmp.r = double(vote_list[i])/neuronNum*3; //*3 for visulaization tmp.n = i+1; locationTree.append(tmp); } export_listNeuron_2swc(locationTree, "./testlocation.swc"); printf("(2). compute adjacency matrix (vote for edges).\n"); double * adjMatrix; V3DLONG * plist; V3DLONG num_nodes = node_list.size(); try{ adjMatrix = new double[num_nodes*num_nodes]; plist = new V3DLONG[num_nodes]; for (V3DLONG i=0;i<num_nodes*num_nodes;i++) adjMatrix[i] = 0; } catch (...) { fprintf(stderr,"fail to allocate memory.\n"); if (adjMatrix) {delete[] adjMatrix; adjMatrix=0;} if (plist) {delete[] plist; plist=0;} return false; } for (int i=0;i<neuronNum;i++) { QHash<V3DLONG, V3DLONG > nodeMap; for (V3DLONG j=0;j<nt_list[i].listNeuron.size();j++) { NeuronSWC s = nt_list[i].listNeuron.at(j); Point3D cur; cur.x = s.x; cur.y = s.y; cur.z = s.z; //find its nearest node V3DLONG node_id = -1;// this node does not exist double min_dis = CLUSTERING_RANGE; //threshold to ignore mapping (too far away) for (V3DLONG ni = 0; ni <node_list.size(); ni++) { Point3D p = node_list[ni]; double dis = PointDistance(p,cur); if (dis < min_dis){ min_dis = dis; node_id = ni; } } if (node_id > -1){ nodeMap.insert( j, node_id); } } //maps.push_back(nodeMap); for (V3DLONG j=0;j<nt_list[i].listNeuron.size();j++) { NeuronSWC cur = nt_list[i].listNeuron[j]; // if (cur.pn<0) continue; V3DLONG n_id,pn_id; n_id = nodeMap[j]; if (n_id > 0){ V3DLONG pidx = cur.pn-1;//nt_list[i].hashNeuron.value(cur.pn); // find the index in nueon_list pn_id = nodeMap[pidx]; if (pn_id > 0){ adjMatrix[n_id*num_nodes + pn_id] += 1; adjMatrix[pn_id*num_nodes + n_id] += 1; //cout<<adjMatrix[n_id*num_nodes + pn_id] <<endl; } } } } if (method_code ==0 ){ long rootnode =100; printf("(3). computing minimum-spanning tree.\n"); //if (!mst_dij(adjMatrix, num_nodes, plist, rootnode)) if (!mst_prim(adjMatrix, num_nodes, plist, rootnode)) { fprintf(stderr,"Error in minimum spanning tree!\n"); return false; } printf("(3). genearate consensus graph swc file by assign parents (form edges).\n"); // code the edge votes into type for visualization // graph: duplicate swc nodes are allowed to accomandate mutiple parents for the child node, no root id, merge_result.clear(); for (V3DLONG i = 0;i <num_nodes;i ++) { V3DLONG p = plist[i]; //cout <<p<<endl; unsigned int edgeVote = adjMatrix[i*num_nodes + p]; NeuronSWC tmp; tmp.x = node_list[i].x; tmp.y = node_list[i].y; tmp.z = node_list[i].z; tmp.type = edgeVote; //edge votes tmp.pn = p + 1; //parent id, form the edge tmp.r = double(vote_list[i])/double(neuronNum); tmp.n = i+1; merge_result.append(tmp); } } //### output vertices ( node lcoations) // V3DLONG count = 0; // for (V3DLONG i=0;i<num_nodes;i++) // { // NeuronSWC tmp; // tmp.x = node_list[i].x; // tmp.y = node_list[i].y; // tmp.z = node_list[i].z; // // tmp.fea_val.push_back(vote_list[i]); //location votes are coded into radius // tmp.type = 0; //vertices (soma) // tmp.pn = -1; //parent id, no edge // tmp.r = double(vote_list[i])/double(neuronNum); //location votes are coded into radius // tmp.n = count +1; //id start from 1 // merge_result.append(tmp); // count++; // } // //output edges, go through half of the symmetric matrix, not directed graph // for (V3DLONG row = 0;row <num_nodes;row ++) // { // for (V3DLONG col = row+1;col < num_nodes;col++){ // unsigned int edgeVote = adjMatrix[row*num_nodes + col]; // if (edgeVote > 1) // { // NeuronSWC tmp; // tmp.x = node_list[row].x; // tmp.y = node_list[row].y; // tmp.z = node_list[row].z; // tmp.type = edgeVote; //edge votes // tmp.pn = col + 1; //parent id , form the edge // tmp.r = double(vote_list[row])/double(neuronNum); // tmp.n = count+1; // merge_result.append(tmp); // count++; // } // } // } // alternative if (method_code == 1){ merge_result.clear(); V3DLONG count = 0; for (V3DLONG i=0;i<num_nodes;i++) { NeuronSWC tmp; tmp.x = node_list[i].x; tmp.y = node_list[i].y; tmp.z = node_list[i].z; // tmp.fea_val.push_back(vote_list[i]); //location votes are coded into radius tmp.type = 0; //vertices (soma) tmp.pn = -1; //parent id, no edge tmp.r = double(vote_list[i])/double(neuronNum); //location votes are coded into radius tmp.n = count +1; //id start from 1 merge_result.append(tmp); count++; } //output edges, go through half of the symmetric matrix, not directed graph for (V3DLONG row = 0;row <num_nodes;row ++) { for (V3DLONG col = row+1;col < num_nodes;col++){ unsigned int edgeVote = adjMatrix[row*num_nodes + col]; if (edgeVote > 0) { if (merge_result[row].pn == -1) {//exsiting isolated vertex, modify parent id merge_result[row].type = edgeVote; //edge votes merge_result[row].pn = col + 1; //parent id , form the edge merge_result[row].r = double(vote_list[row])/double(neuronNum); } else{ //add new edge , via duplication nodes with different parent id and edge votes NeuronSWC tmp; tmp.x = node_list[row].x; tmp.y = node_list[row].y; tmp.z = node_list[row].z; tmp.type = edgeVote; //edge votes tmp.pn = col + 1; //parent id , form the edge tmp.r = double(vote_list[row])/double(neuronNum); tmp.n = count+1; merge_result.append(tmp); count++; } } } } } if (adjMatrix) {delete[] adjMatrix; adjMatrix = 0;} if (plist) {delete[] plist; plist=0;} return true; }
//return a list of predictions ? Or a mask image? // Also need numbers of cells, and centers of cells //work on local maxima //use different preprocessing, feature extraction etc. //1d_dimension is ROIDImension + 1 //Note: be aware of the order x, y, z are passed to submethods. unsigned char* CellClassifier::annotateAnImage(V3DPluginCallback &callback, v3dhandle win, Image4DSimple* image, int ch, int grid, int dimension_1d) { std::vector <LocationSimple> detectedPos; LandmarkList markerList; long sx=image->sz0, sy=image->sz1, sz=image->sz2; long pagesz=sx*sy; long channelsz=sx*sy*sz; //smoothed and get local maxima //sensitive gaussian filtering radius // ??????? use distance transform image??????? int Wx = dimension_1d, Wy = dimension_1d, Wz = dimension_1d; unsigned char * filtered = gaussianfiltering(image->getRawData(), sx, sy, sz, Wx, Wy, Wz); unsigned char * flag_lm = check_localMaxima_ker(filtered, sx, sy, sz, 0); //unsigned char * flag_lm = check_localMaxima(callback, image, ch); //062110 unsigned char *results = new unsigned char [channelsz]; //prediction results. if (!results) { printf("Fail to allocate memory.\n"); return NULL; } unsigned char *clearFlag = new unsigned char [channelsz]; //prediction results. if (!clearFlag) { printf("Fail to allocate memory.\n"); return NULL; } //get model from file //string modelfile = "test_svm_model.txt"; struct svm_model * pmodel = pclassifier->loadSVMModel(); int r = dimension_1d/2; int prediction; int total = 0, cellcount = 0; //use (a somehow more restrict) template matching for adding some candidates. bool candidateflag = false; long cubesize = dimension_1d*dimension_1d*dimension_1d; double sigmax = (dimension_1d-1)/4.0, sigmay = (dimension_1d-1)/4.0, sigmaz = (dimension_1d-1)/4.0; double *g_1d = genGaussianKernel1D(dimension_1d, dimension_1d, dimension_1d, sigmax, sigmay, sigmaz); double curcoeff; //double coeff_th = 0.66; //will use average coeff of positive ones from training!!!! //init results for(long iz = 0; iz < sz; iz++) { long offsetk = iz*sx*sy; for(long iy = 0; iy < sy; iy++) { long offsetj = iy*sx; for(long ix = 0; ix < sx; ix++) { long idx = offsetk + offsetj + ix; results[idx] = 0; clearFlag[idx] = 1; //will be cleared later } } } //start search int denied = 0; for(long iz = r; iz < sz-r; iz++) { long offsetk = iz*sx*sy; for(long iy = r; iy < sy-r; iy++) { long offsetj = iy*sx; for(long ix = r; ix < sx-r; ix++) { long idx = offsetk + offsetj + ix; //debug: //if(ix == 61 && iy==39 && iz==17) cout << "the missed one is here!!" << (int) flag_lm[idx] << endl; //if(ix == 63 && iy ==43 && iz ==16) cout << "the valley one (#3) is here!!" << (int) flag_lm[idx] << endl;; //if(ix == 65 && iy ==43 && iz ==14) cout << "the valley start point is here!!" << (int) flag_lm[idx] << endl;; if(flag_lm[idx] == 255 && clearFlag[idx] == 1 ) //only work on local maxima (and not cleared) { //classify to see if it is a center prediction = classifyAVoxel(image, ch, iz, iy, ix, dimension_1d, pmodel); if(prediction == 0) denied ++; candidateflag = false; if (prediction == 1 || candidateflag) { //cellcount ++; //results[idx] = 255; // double ncx, ncy, ncz; unsigned char * currentCube =getACube(image, ch, iz, iy, ix, dimension_1d, dimension_1d*dimension_1d, pagesz, channelsz); //fixed a bug 060210 curcoeff = coeff(currentCube, g_1d, cubesize); //debug if(ix == 65 && iy ==43 && iz ==14) std::cout << "coeff at valley:" << curcoeff << std::endl ; delete[] currentCube; //if(curcoeff < 0.45) //{ // std::cout << "coeff too low for (" << ix << " " << iy << " " << iz << ") " << " coef: " << curcoeff << endl; // continue; //} //converge toward center of mass, //move the center using highest score (either matching or classifier). /* getMatchingCenter(image, ix, iy, iz, r, ch, ncx, ncy, ncz, flag_lm); double nncx, nncy, nncz; //getMassCenter(image, ncx, ncy, ncz, r, ch, nncx, nncy, nncz, flag_lm); getMassCenter(filtered, sx,sy,sz, ncx, ncy, ncz, r, ch, nncx, nncy, nncz, flag_lm); int newx = (int) (nncx+0.5), newy = (int) (nncy+0.5), newz = (int) (nncz+0.5); //rounding */ getMassCenter(filtered, sx,sy,sz, ix, iy, iz, r, ch, ncx, ncy, ncz, flag_lm); int newx = (int) (ncx+0.5), newy = (int) (ncy+0.5), newz = (int) (ncz+0.5); //rounding //make a final decision if, after moving, there is a close neighbor identified already on the new spot if (nearACenter(newx, newy, newz, results, r, sx, sy, sz)) { // std::cout << "#" << cellcount << ", there's 1 nearby. Skip. " ; continue; } long foundidx = newz*sx*sy + newy*sx + newx; //check if move to a lm , possible for valley area between cells. //why this made the debug image has 20 down to 9 cells!! /*if ( flag_lm[foundidx] == 0) { std::cout << "non-lm. Skip. " ; continue; }*/ //else: a cell is found, set that to 255 results[foundidx] = 255; //remove foreground around that center from image: based on radius? reestimate based on std? //int testt=0; for(int lll =0; lll < channelsz; lll++) if (flag_lm[lll] ==255) testt ++; //cout << "postive flags: " << testt << endl; //will not consider its neighbors in the future. clearSurrounding(image, ch, newx, newy, newz, r, clearFlag); //testt=0; for(int lll =0; lll < channelsz; lll++) if (flag_lm[lll] ==255) testt ++; //cout << "postive flags after: " << testt << endl; //increase cell counter by 1. LocationSimple pp(newx, newy, newz); detectedPos.push_back(pp); LocationSimple marker(newx +1, newy +1, newz +1); //convert back to 1-base marker.radius = r; markerList.push_back(marker); cellcount ++; } }//end local maxima }//end ix }//end iy }//end iz std::cout << "total number of cells:" << cellcount << std::endl; char buf[30]; itoa(cellcount, buf, 10); string resstring = "total cells:"; resstring.append(buf); QMessageBox::information(0, "debug", QString(resstring.c_str())); std::cout << "total denied local maximum:" << denied << std::endl; svm_destroy_model(pmodel); /* //results only Image4DSimple p4DImage; p4DImage.setData(results, sx, sy, sz, 1, image->datatype); v3dhandle newwin = callback.newImageWindow(); callback.setImage(newwin, &p4DImage); callback.setImageName(newwin, "prediction results image"); callback.updateImageWindow(newwin); */ //v3d does not allow me to use the same image data in a different (new) window? //I need to make a copy of the data first? unsigned char *newimage1d = new unsigned char [channelsz]; if(!newimage1d) { std::cout << "not enough memory to create result window"; return results; } unsigned char* image1d = image->getRawData(); for(long n = 0; n < channelsz; n++) newimage1d[n] = image1d[ch*channelsz + n]; //show resulting cell markers in a new window Image4DSimple newImage; newImage.setData(newimage1d, sx, sy, sz, 1, image->datatype); v3dhandle newwin = callback.newImageWindow(); callback.setImage(newwin, &newImage); callback.setImageName(newwin, "cell_counted"); callback.updateImageWindow(newwin); callback.setLandmark(newwin, markerList); //de-alloc //should I close svm file here??? if(filtered) { std::cout << "clean up memory for filtered image." << endl; delete[] filtered; } if(flag_lm) { std::cout << "clean up memory for local maximum." << endl; delete[] flag_lm; } if(clearFlag) { std::cout << "clean up memory for flags." << endl; delete[] clearFlag; } return results; }
bool findedgeimg(V3DPluginCallback2 &callback, const V3DPluginArgList & input, V3DPluginArgList & output) { cout<<"Welcome to Label edge of a mask image"<<endl; if (output.size() != 1) return false; int method_code = 0; if (input.size()>=2) { vector<char*> paras = (*(vector<char*> *)(input.at(1).p)); if(paras.size() >= 1) method_code = atoi(paras.at(0)); } char * inimg_file = ((vector<char*> *)(input.at(0).p))->at(0); char * outimg_file = ((vector<char*> *)(output.at(0).p))->at(0); cout<<"method_code = "<<method_code<<endl; cout<<"inimg_file = "<<inimg_file<<endl; cout<<"outimg_file = "<<outimg_file<<endl; Image4DSimple *image = callback.loadImage(inimg_file); if (!image || !image->valid()) { cerr<<"load image "<<inimg_file<<" error!"<<endl; return false; } V3DLONG szx=image->getXDim(), szy=image->getYDim(), szz=image->getZDim(), szc=image->getCDim(); V3DLONG N = image->getTotalUnitNumber(); //create the output buffer unsigned char *outputData = 0; try { outputData = new unsigned char [N]; for (V3DLONG tmpi=0; tmpi<N; ++tmpi) outputData[tmpi] = 0; //preset to be all 0 } catch (...) { v3d_msg("Fail to allocate memory."); if (outputData) { delete []outputData; outputData=0; } return false; } Image4DSimple outputImage; outputImage.setData((unsigned char*)outputData, szx, szy, szz, szc, V3D_UINT8); Image4DProxy<Image4DSimple> outputIProxy(&outputImage); //now do computation { bool bset255 = (method_code==0) ? false : true; Image4DProxy<Image4DSimple> p(image); Image4DProxy_foreach(p, ix, iy, iz, ic) { double v = p.value_at(ix, iy, iz, ic); V3DLONG cx, cy, cz; bool bb=false; for (cz = iz-1; cz<iz+2; ++cz) { for (cy = iy-1; cy<iy+2; ++cy) { for (cx = ix-1; cx<ix+2; ++cx) { if (!p.is_inner(cx, cy, cz, ic)) continue; if (v!=p.value_at(cx, cy, cz, ic)) { *outputIProxy.at(ix, iy, iz, ic) = (bset255) ? 255 : v; bb = true; break; } } if (bb) break; } if (bb) break; } //note that all value had been preset as 0, thus no need to set as the background color in case not an edge point } }